langflow-base-nightly 0.5.0.dev36__py3-none-any.whl → 0.5.0.dev38__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.
- langflow/__main__.py +1 -1
- langflow/alembic/versions/4e5980a44eaa_fix_date_times_again.py +24 -30
- langflow/alembic/versions/58b28437a398_modify_nullable.py +6 -6
- langflow/alembic/versions/79e675cb6752_change_datetime_type.py +24 -30
- langflow/alembic/versions/b2fa308044b5_add_unique_constraints.py +12 -13
- langflow/api/build.py +21 -26
- langflow/api/health_check_router.py +3 -3
- langflow/api/utils.py +3 -3
- langflow/api/v1/callback.py +2 -2
- langflow/api/v1/chat.py +19 -31
- langflow/api/v1/endpoints.py +10 -10
- langflow/api/v1/flows.py +1 -1
- langflow/api/v1/knowledge_bases.py +19 -12
- langflow/api/v1/mcp.py +12 -12
- langflow/api/v1/mcp_projects.py +45 -81
- langflow/api/v1/mcp_utils.py +8 -8
- langflow/api/v1/schemas.py +1 -5
- langflow/api/v1/store.py +1 -1
- langflow/api/v1/validate.py +2 -2
- langflow/api/v1/voice_mode.py +58 -62
- langflow/api/v2/files.py +5 -3
- langflow/api/v2/mcp.py +10 -9
- langflow/base/composio/composio_base.py +21 -2
- langflow/base/data/docling_utils.py +194 -0
- langflow/base/data/kb_utils.py +33 -0
- langflow/base/embeddings/aiml_embeddings.py +1 -1
- langflow/base/flow_processing/utils.py +1 -2
- langflow/base/io/__init__.py +0 -1
- langflow/base/langwatch/utils.py +2 -1
- langflow/base/mcp/util.py +49 -47
- langflow/base/models/model.py +3 -3
- langflow/base/prompts/api_utils.py +1 -1
- langflow/base/tools/flow_tool.py +2 -2
- langflow/base/tools/run_flow.py +2 -6
- langflow/components/Notion/add_content_to_page.py +2 -2
- langflow/components/Notion/list_database_properties.py +2 -2
- langflow/components/Notion/list_pages.py +2 -2
- langflow/components/Notion/page_content_viewer.py +2 -2
- langflow/components/Notion/update_page_property.py +1 -1
- langflow/components/agentql/agentql_api.py +2 -10
- langflow/components/agents/agent.py +3 -3
- langflow/components/agents/mcp_component.py +54 -69
- langflow/components/anthropic/anthropic.py +5 -4
- langflow/components/assemblyai/assemblyai_get_subtitles.py +2 -2
- langflow/components/assemblyai/assemblyai_lemur.py +2 -2
- langflow/components/assemblyai/assemblyai_list_transcripts.py +2 -2
- langflow/components/assemblyai/assemblyai_poll_transcript.py +2 -2
- langflow/components/assemblyai/assemblyai_start_transcript.py +2 -2
- langflow/components/data/file.py +575 -55
- langflow/components/data/kb_ingest.py +116 -43
- langflow/components/data/kb_retrieval.py +24 -26
- langflow/components/data/url.py +1 -1
- langflow/components/datastax/astra_assistant_manager.py +3 -3
- langflow/components/datastax/create_assistant.py +1 -2
- langflow/components/deactivated/merge_data.py +1 -2
- langflow/components/deactivated/sub_flow.py +6 -7
- langflow/components/deactivated/vectara_self_query.py +3 -3
- langflow/components/docling/__init__.py +0 -198
- langflow/components/docling/docling_inline.py +1 -1
- langflow/components/embeddings/text_embedder.py +3 -3
- langflow/components/firecrawl/firecrawl_extract_api.py +2 -9
- langflow/components/google/gmail.py +1 -1
- langflow/components/google/google_generative_ai.py +5 -11
- langflow/components/groq/groq.py +4 -3
- langflow/components/helpers/current_date.py +2 -3
- langflow/components/helpers/memory.py +1 -1
- langflow/components/ibm/watsonx.py +1 -1
- langflow/components/ibm/watsonx_embeddings.py +1 -1
- langflow/components/langwatch/langwatch.py +3 -3
- langflow/components/logic/flow_tool.py +2 -2
- langflow/components/logic/notify.py +1 -1
- langflow/components/logic/run_flow.py +2 -3
- langflow/components/logic/sub_flow.py +4 -5
- langflow/components/mem0/mem0_chat_memory.py +2 -8
- langflow/components/nvidia/nvidia.py +3 -3
- langflow/components/olivya/olivya.py +7 -7
- langflow/components/ollama/ollama.py +8 -6
- langflow/components/processing/batch_run.py +8 -8
- langflow/components/processing/data_operations.py +2 -2
- langflow/components/processing/merge_data.py +1 -2
- langflow/components/processing/message_to_data.py +2 -3
- langflow/components/processing/parse_json_data.py +1 -1
- langflow/components/processing/save_file.py +6 -32
- langflow/components/prototypes/python_function.py +2 -3
- langflow/components/serpapi/serp.py +1 -1
- langflow/components/tavily/tavily_extract.py +1 -1
- langflow/components/tavily/tavily_search.py +1 -1
- langflow/components/tools/calculator.py +2 -2
- langflow/components/tools/python_code_structured_tool.py +3 -10
- langflow/components/tools/python_repl.py +2 -2
- langflow/components/tools/searxng.py +3 -3
- langflow/components/tools/serp_api.py +2 -2
- langflow/components/tools/tavily_search_tool.py +2 -2
- langflow/components/tools/yahoo_finance.py +1 -1
- langflow/components/twelvelabs/video_embeddings.py +4 -4
- langflow/components/vectorstores/astradb.py +30 -19
- langflow/components/vectorstores/local_db.py +1 -1
- langflow/components/yahoosearch/yahoo.py +1 -1
- langflow/components/youtube/trending.py +3 -4
- langflow/custom/attributes.py +2 -1
- langflow/custom/code_parser/code_parser.py +1 -1
- langflow/custom/custom_component/base_component.py +1 -1
- langflow/custom/custom_component/component.py +16 -2
- langflow/custom/directory_reader/directory_reader.py +7 -7
- langflow/custom/directory_reader/utils.py +1 -2
- langflow/custom/utils.py +30 -30
- langflow/events/event_manager.py +1 -1
- langflow/frontend/assets/{SlackIcon-B260Qg_R.js → SlackIcon-BhW6H3JR.js} +1 -1
- langflow/frontend/assets/{Wikipedia-BB2mbgyd.js → Wikipedia-Dx5jbiy3.js} +1 -1
- langflow/frontend/assets/{Wolfram-DytXC9hF.js → Wolfram-CIyonzwo.js} +1 -1
- langflow/frontend/assets/{index-DPX6X_bw.js → index-0XQqYgdG.js} +1 -1
- langflow/frontend/assets/{index-DtJyCbzF.js → index-1Q3VBqKn.js} +1 -1
- langflow/frontend/assets/{index-DztLFiip.js → index-35sspuLu.js} +1 -1
- langflow/frontend/assets/{index-BeNby7qF.js → index-7hzXChQz.js} +1 -1
- langflow/frontend/assets/{index-BOEf7-ty.js → index-8cuhogZP.js} +1 -1
- langflow/frontend/assets/{index-D0s9f6Re.js → index-B0m53xKd.js} +1 -1
- langflow/frontend/assets/{index-DpJiH-Rk.js → index-B1XqWJhG.js} +1 -1
- langflow/frontend/assets/{index-DuAeoC-H.js → index-B3KCdQ91.js} +1 -1
- langflow/frontend/assets/{index-Bxml6wXu.js → index-B7uEuOPK.js} +1 -1
- langflow/frontend/assets/{index-CDFLVFB4.js → index-B8UR8v-Q.js} +1 -1
- langflow/frontend/assets/{index-ci4XHjbJ.js → index-BD7Io1hL.js} +6 -6
- langflow/frontend/assets/{index-DasrI03Y.js → index-BDQrd7Tj.js} +1 -1
- langflow/frontend/assets/{index-CkQ-bJ4G.js → index-BDuk0d7P.js} +1 -1
- langflow/frontend/assets/{index-C_1RBTul.js → index-BFQ8KFK0.js} +1 -1
- langflow/frontend/assets/{index-DqSH4x-R.js → index-BFf0HTFI.js} +1 -1
- langflow/frontend/assets/{index-BXMhmvTj.js → index-BHhnpSkW.js} +1 -1
- langflow/frontend/assets/{index-Uq2ij_SS.js → index-BKKrUElc.js} +1 -1
- langflow/frontend/assets/{index-3TJWUdmx.js → index-BKeZt2hQ.js} +1 -1
- langflow/frontend/assets/{index-DHlEwAxb.js → index-BKlQbl-6.js} +1 -1
- langflow/frontend/assets/{index-Bisa4IQF.js → index-BLYw9MK2.js} +1 -1
- langflow/frontend/assets/{index-GODbXlHC.js → index-BLsVo9iW.js} +1 -1
- langflow/frontend/assets/{index-CHFO5O4g.js → index-BNQIbda3.js} +1 -1
- langflow/frontend/assets/{index-3uOAA_XX.js → index-BPR2mEFC.js} +1 -1
- langflow/frontend/assets/{index-3qMh9x6K.js → index-BPfdqCc_.js} +1 -1
- langflow/frontend/assets/{index-rcdQpNcU.js → index-BQrVDjR1.js} +1 -1
- langflow/frontend/assets/{index-4eRtaV45.js → index-BRmSeoWR.js} +1 -1
- langflow/frontend/assets/{index-Ct9_T9ox.js → index-BUse-kxM.js} +1 -1
- langflow/frontend/assets/{index-BdYgKk1d.js → index-BVFaF7HW.js} +1 -1
- langflow/frontend/assets/{index-CWWo2zOA.js → index-BWgIWfv2.js} +1 -1
- langflow/frontend/assets/{index-Du9aJK7m.js → index-BWt5xGeA.js} +1 -1
- langflow/frontend/assets/{index-Baka5dKE.js → index-BYhcGLTV.js} +1 -1
- langflow/frontend/assets/{index-BWq9GTzt.js → index-BYjw7Gk3.js} +1 -1
- langflow/frontend/assets/{index-r1LZg-PY.js → index-BZFljdMa.js} +1 -1
- langflow/frontend/assets/index-BcAgItH4.js +1 -0
- langflow/frontend/assets/{index-B8TlNgn-.js → index-Bct1s6__.js} +1 -1
- langflow/frontend/assets/{index-DZzbmg3J.js → index-Bhv79Zso.js} +1 -1
- langflow/frontend/assets/{index-CqDUqHfd.js → index-Bj3lSwvZ.js} +1 -1
- langflow/frontend/assets/{index-dkS0ek2S.js → index-Bk4mTwnI.js} +1 -1
- langflow/frontend/assets/{index-tOy_uloT.js → index-BmIx1cws.js} +1 -1
- langflow/frontend/assets/{index-BVtf6m9S.js → index-BmYJJ5YS.js} +1 -1
- langflow/frontend/assets/{index-mBjJYD9q.js → index-BnAFhkSN.js} +1 -1
- langflow/frontend/assets/{index-Ba3RTMXI.js → index-Bo-ww0Bb.js} +1 -1
- langflow/frontend/assets/{index-BsBWP-Dh.js → index-BpmqDOeZ.js} +1 -1
- langflow/frontend/assets/{index-BqUeOc7Y.js → index-BrVhdPZb.js} +1 -1
- langflow/frontend/assets/{index-DWkMJnbd.js → index-BvGQfVBD.js} +1 -1
- langflow/frontend/assets/{index-DdzVmJHE.js → index-Bwi4flFg.js} +1 -1
- langflow/frontend/assets/{index-Ccb5B8zG.js → index-BzoRPtTY.js} +1 -1
- langflow/frontend/assets/{index-Ym6gz0T6.js → index-C--IDAyc.js} +1 -1
- langflow/frontend/assets/{index-CvQ0w8Pj.js → index-C0E3_MIK.js} +1 -1
- langflow/frontend/assets/{index-DxIs8VSp.js → index-C27Jj_26.js} +1 -1
- langflow/frontend/assets/{index-BxWXWRmZ.js → index-C2eQmQsn.js} +1 -1
- langflow/frontend/assets/{index-B536IPXH.js → index-C8K0r39B.js} +1 -1
- langflow/frontend/assets/{index-BEDxAk3N.js → index-CEJNWPhA.js} +1 -1
- langflow/frontend/assets/{index-G_U_kPAd.js → index-CFNTYfFK.js} +1 -1
- langflow/frontend/assets/{index-CMGZGIx_.js → index-CMHpjHZl.js} +1 -1
- langflow/frontend/assets/{index-C76aBV_h.js → index-CSu8KHOi.js} +1 -1
- langflow/frontend/assets/{index-B-c82Fnu.js → index-CUKmGsI6.js} +1 -1
- langflow/frontend/assets/{index-DX7XsAcx.js → index-CWYiSeWV.js} +1 -1
- langflow/frontend/assets/{index-COL0eiWI.js → index-CY7_TBTC.js} +1 -1
- langflow/frontend/assets/{index-BlBl2tvQ.js → index-CbnWRlYY.js} +1 -1
- langflow/frontend/assets/{index-BQB-iDYl.js → index-CfPBgkqg.js} +1 -1
- langflow/frontend/assets/{index-DWr_zPkx.js → index-Cg53lrYh.js} +1 -1
- langflow/frontend/assets/{index-BcgB3rXH.js → index-CgU7KF4I.js} +1 -1
- langflow/frontend/assets/{index-CkSzjCqM.js → index-CgwykVGh.js} +1 -1
- langflow/frontend/assets/{index-BbsND1Qg.js → index-Ch5r0oW6.js} +1 -1
- langflow/frontend/assets/{index-AY5Dm2mG.js → index-CjsommIr.js} +1 -1
- langflow/frontend/assets/{index-BtJ2o21k.js → index-CkK25zZO.js} +1 -1
- langflow/frontend/assets/{index-BKvKC-12.js → index-CkjwSTSM.js} +1 -1
- langflow/frontend/assets/{index-BVHvIhT5.js → index-CmSFKgiD.js} +1 -1
- langflow/frontend/assets/{index-D-zkHcob.js → index-Cr5v2ave.js} +1 -1
- langflow/frontend/assets/{index-js8ceOaP.js → index-CrAF-31Y.js} +1 -1
- langflow/frontend/assets/{index-BNbWMmAV.js → index-CsLQiWNf.js} +1 -1
- langflow/frontend/assets/{index-VcXZzovW.js → index-CuCM7Wu7.js} +1 -1
- langflow/frontend/assets/{index-DzeIsaBm.js → index-Cxy9sEpy.js} +1 -1
- langflow/frontend/assets/{index-LrMzDsq9.js → index-CyP3py8K.js} +1 -1
- langflow/frontend/assets/{index-C8KD3LPb.js → index-CzHzeZuA.js} +1 -1
- langflow/frontend/assets/{index-DS1EgA10.js → index-D1oynC8a.js} +1 -1
- langflow/frontend/assets/{index-ByFXr9Iq.js → index-D4tjMhfY.js} +1 -1
- langflow/frontend/assets/{index-DyJDHm2D.js → index-D6CSIrp1.js} +1 -1
- langflow/frontend/assets/{index-DIqSyDVO.js → index-D9kwEzPB.js} +1 -1
- langflow/frontend/assets/{index-D5PeCofu.js → index-DDXsm8tz.js} +1 -1
- langflow/frontend/assets/{index-CJwYfDBz.js → index-DDhJVVel.js} +1 -1
- langflow/frontend/assets/{index-C7x9R_Yo.js → index-DH6o91_s.js} +1 -1
- langflow/frontend/assets/{index-DpQKtcXu.js → index-DHngW1k8.js} +1 -1
- langflow/frontend/assets/{index-VZnN0P6C.js → index-DIKUsGLF.js} +1 -1
- langflow/frontend/assets/{index-VHmUHUUU.js → index-DJESSNJi.js} +1 -1
- langflow/frontend/assets/{index-BdIWbCEL.js → index-DMCWDJOl.js} +1 -1
- langflow/frontend/assets/{index-DK8vNpXK.js → index-DOEvKC2X.js} +1 -1
- langflow/frontend/assets/{index-C7V5U9yH.js → index-DOQDkSoK.js} +1 -1
- langflow/frontend/assets/{index-D0HmkH0H.js → index-DXAfIEvs.js} +1 -1
- langflow/frontend/assets/{index-C9N80hP8.js → index-DZP_SaHb.js} +1 -1
- langflow/frontend/assets/{index-B2ggrBuR.js → index-DZxUIhWh.js} +1 -1
- langflow/frontend/assets/{index-DS9I4y48.js → index-Dda2u_yz.js} +1 -1
- langflow/frontend/assets/{index-BLROcaSz.js → index-Dg8N3NSO.js} +1 -1
- langflow/frontend/assets/{index-Dpz3oBf5.js → index-DkGhPNeA.js} +1 -1
- langflow/frontend/assets/{index-BnLT29qW.js → index-Dka_Rk4-.js} +1 -1
- langflow/frontend/assets/{index-B5ed-sAv.js → index-DljpLeCW.js} +1 -1
- langflow/frontend/assets/{index-Cx__T92e.js → index-DnVYJtVO.js} +1 -1
- langflow/frontend/assets/{index-hOkEW3JP.js → index-DqbzUcI5.js} +1 -1
- langflow/frontend/assets/{index-BxkZkBgQ.js → index-Dr6pVDPI.js} +1 -1
- langflow/frontend/assets/{index-BIkqesA-.js → index-DsoX2o1S.js} +1 -1
- langflow/frontend/assets/{index-Cpgkb0Q3.js → index-DwfHWnX7.js} +1 -1
- langflow/frontend/assets/{index-B9Mo3ndZ.js → index-Dx-Z87KT.js} +1 -1
- langflow/frontend/assets/{index-R7q8cAek.js → index-DyqITq51.js} +1 -1
- langflow/frontend/assets/{index-DKEXZFUO.js → index-DzIv3RyR.js} +1 -1
- langflow/frontend/assets/{index-BJrY2Fiu.js → index-G4ro0MjT.js} +1 -1
- langflow/frontend/assets/{index-IFGgPiye.js → index-H7J7w7fa.js} +1 -1
- langflow/frontend/assets/{index-lKEJpUsF.js → index-KWY77KfV.js} +1 -1
- langflow/frontend/assets/{index-DDNNv4C0.js → index-U9GWm1eH.js} +1 -1
- langflow/frontend/assets/{index-BRWNIt9F.js → index-Un9pWxnP.js} +1 -1
- langflow/frontend/assets/{index-BCK-ZyIh.js → index-Xi4TplbI.js} +1 -1
- langflow/frontend/assets/{index-BEKoRwsX.js → index-_cbGmjF4.js} +1 -1
- langflow/frontend/assets/{index-7xXgqu09.js → index-cEXY6V06.js} +1 -1
- langflow/frontend/assets/{index-D87Zw62M.js → index-dyXKnkMi.js} +1 -1
- langflow/frontend/assets/{index-CG7cp0nD.js → index-eUkS6iJM.js} +1 -1
- langflow/frontend/assets/{index-CoUlHbtg.js → index-ekfMOqrF.js} +1 -1
- langflow/frontend/assets/{index-DhzEUXfr.js → index-gdb7XMS8.js} +1 -1
- langflow/frontend/assets/{index-D9eflZfP.js → index-hZUcL0MZ.js} +1 -1
- langflow/frontend/assets/{index-CwIxqYlT.js → index-kkA-qHB_.js} +1 -1
- langflow/frontend/assets/{index-sS6XLk3j.js → index-mzl9ULw5.js} +1 -1
- langflow/frontend/assets/{index-BjENqyKe.js → index-oxHBZk2v.js} +1 -1
- langflow/frontend/assets/{index-BejHxU5W.js → index-p2kStSPe.js} +1 -1
- langflow/frontend/assets/{index-BOYTBrh9.js → index-paQEWYGT.js} +1 -1
- langflow/frontend/assets/{index-Cd5zuUUK.js → index-r_8gs4nL.js} +1 -1
- langflow/frontend/assets/{index-AlJ7td-D.js → index-uiKla4UR.js} +1 -1
- langflow/frontend/assets/{index-B8y58M9b.js → index-vJOO5U8M.js} +1 -1
- langflow/frontend/assets/{index-CF4dtI6S.js → index-w72fDjpG.js} +1 -1
- langflow/frontend/assets/{index-C2Xd7UkR.js → index-zV82kQ6k.js} +1 -1
- langflow/frontend/assets/lazyIconImports-DTNgvPE-.js +2 -0
- langflow/frontend/assets/{use-post-add-user-HN0rRnhv.js → use-post-add-user-CvtuazTg.js} +1 -1
- langflow/frontend/index.html +1 -1
- langflow/graph/edge/base.py +2 -3
- langflow/graph/graph/base.py +14 -12
- langflow/graph/graph/constants.py +3 -0
- langflow/graph/utils.py +6 -6
- langflow/graph/vertex/base.py +4 -5
- langflow/graph/vertex/param_handler.py +1 -1
- langflow/graph/vertex/vertex_types.py +2 -2
- langflow/helpers/flow.py +1 -1
- langflow/initial_setup/setup.py +32 -30
- langflow/initial_setup/starter_projects/Blog Writer.json +2 -2
- langflow/initial_setup/starter_projects/Custom Component Generator.json +2 -2
- langflow/initial_setup/starter_projects/Document Q&A.json +1 -1
- langflow/initial_setup/starter_projects/Hybrid Search RAG.json +2 -2
- langflow/initial_setup/starter_projects/Instagram Copywriter.json +3 -3
- langflow/initial_setup/starter_projects/Invoice Summarizer.json +1 -1
- langflow/initial_setup/starter_projects/Knowledge Ingestion.json +4 -4
- langflow/initial_setup/starter_projects/Knowledge Retrieval.json +2 -2
- langflow/initial_setup/starter_projects/Market Research.json +3 -3
- langflow/initial_setup/starter_projects/Meeting Summary.json +6 -6
- langflow/initial_setup/starter_projects/Memory Chatbot.json +2 -2
- langflow/initial_setup/starter_projects/News Aggregator.json +5 -22
- langflow/initial_setup/starter_projects/Nvidia Remix.json +3 -20
- langflow/initial_setup/starter_projects/Pok/303/251dex Agent.json" +1 -1
- langflow/initial_setup/starter_projects/Portfolio Website Code Generator.json +1 -1
- langflow/initial_setup/starter_projects/Price Deal Finder.json +5 -5
- langflow/initial_setup/starter_projects/Research Agent.json +3 -3
- langflow/initial_setup/starter_projects/SaaS Pricing.json +1 -1
- langflow/initial_setup/starter_projects/Search agent.json +1 -1
- langflow/initial_setup/starter_projects/Sequential Tasks Agents.json +7 -7
- langflow/initial_setup/starter_projects/Simple Agent.json +3 -3
- langflow/initial_setup/starter_projects/Social Media Agent.json +1 -1
- langflow/initial_setup/starter_projects/Text Sentiment Analysis.json +1 -1
- langflow/initial_setup/starter_projects/Travel Planning Agents.json +3 -3
- langflow/initial_setup/starter_projects/Vector Store RAG.json +5 -5
- langflow/initial_setup/starter_projects/Youtube Analysis.json +3 -3
- langflow/interface/components.py +23 -22
- langflow/interface/initialize/loading.py +5 -5
- langflow/interface/run.py +1 -1
- langflow/interface/utils.py +1 -1
- langflow/io/__init__.py +0 -1
- langflow/langflow_launcher.py +1 -1
- langflow/load/load.py +2 -7
- langflow/logging/__init__.py +0 -1
- langflow/logging/logger.py +191 -115
- langflow/logging/setup.py +1 -1
- langflow/main.py +37 -52
- langflow/memory.py +7 -7
- langflow/middleware.py +1 -1
- langflow/processing/process.py +4 -4
- langflow/schema/artifact.py +2 -2
- langflow/schema/data.py +10 -2
- langflow/schema/dataframe.py +1 -1
- langflow/schema/message.py +1 -1
- langflow/serialization/serialization.py +1 -1
- langflow/services/auth/utils.py +2 -2
- langflow/services/cache/disk.py +1 -1
- langflow/services/cache/service.py +3 -3
- langflow/services/database/models/flow/model.py +2 -7
- langflow/services/database/models/transactions/crud.py +2 -2
- langflow/services/database/models/user/crud.py +2 -2
- langflow/services/database/service.py +8 -8
- langflow/services/database/utils.py +6 -5
- langflow/services/deps.py +2 -3
- langflow/services/factory.py +1 -1
- langflow/services/flow/flow_runner.py +7 -12
- langflow/services/job_queue/service.py +16 -15
- langflow/services/manager.py +3 -4
- langflow/services/settings/auth.py +1 -1
- langflow/services/settings/base.py +3 -8
- langflow/services/settings/manager.py +1 -1
- langflow/services/settings/utils.py +1 -1
- langflow/services/socket/__init__.py +0 -1
- langflow/services/socket/service.py +3 -3
- langflow/services/socket/utils.py +4 -4
- langflow/services/state/service.py +1 -2
- langflow/services/storage/factory.py +1 -1
- langflow/services/storage/local.py +9 -8
- langflow/services/storage/s3.py +11 -10
- langflow/services/store/service.py +3 -3
- langflow/services/store/utils.py +3 -2
- langflow/services/task/temp_flow_cleanup.py +7 -7
- langflow/services/telemetry/service.py +10 -10
- langflow/services/tracing/arize_phoenix.py +2 -2
- langflow/services/tracing/langfuse.py +1 -1
- langflow/services/tracing/langsmith.py +1 -1
- langflow/services/tracing/langwatch.py +1 -1
- langflow/services/tracing/opik.py +1 -1
- langflow/services/tracing/service.py +25 -6
- langflow/services/tracing/traceloop.py +245 -0
- langflow/services/utils.py +7 -7
- langflow/services/variable/kubernetes.py +3 -3
- langflow/services/variable/kubernetes_secrets.py +2 -1
- langflow/services/variable/service.py +5 -5
- langflow/utils/component_utils.py +9 -6
- langflow/utils/util.py +5 -5
- langflow/utils/validate.py +3 -3
- langflow/utils/voice_utils.py +2 -2
- {langflow_base_nightly-0.5.0.dev36.dist-info → langflow_base_nightly-0.5.0.dev38.dist-info}/METADATA +2 -1
- {langflow_base_nightly-0.5.0.dev36.dist-info → langflow_base_nightly-0.5.0.dev38.dist-info}/RECORD +342 -340
- langflow/frontend/assets/lazyIconImports-Bh1TFfvH.js +0 -2
- {langflow_base_nightly-0.5.0.dev36.dist-info → langflow_base_nightly-0.5.0.dev38.dist-info}/WHEEL +0 -0
- {langflow_base_nightly-0.5.0.dev36.dist-info → langflow_base_nightly-0.5.0.dev38.dist-info}/entry_points.txt +0 -0
|
@@ -1,4 +1,10 @@
|
|
|
1
|
+
import signal
|
|
2
|
+
import sys
|
|
3
|
+
import traceback
|
|
4
|
+
from contextlib import suppress
|
|
5
|
+
|
|
1
6
|
from docling_core.types.doc import DoclingDocument
|
|
7
|
+
from loguru import logger
|
|
2
8
|
|
|
3
9
|
from langflow.schema.data import Data
|
|
4
10
|
from langflow.schema.dataframe import DataFrame
|
|
@@ -49,3 +55,191 @@ def extract_docling_documents(data_inputs: Data | list[Data] | DataFrame, doc_ke
|
|
|
49
55
|
msg = f"Invalid input type in collection: {e}"
|
|
50
56
|
raise TypeError(msg) from e
|
|
51
57
|
return documents
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def docling_worker(file_paths: list[str], queue, pipeline: str, ocr_engine: str):
|
|
61
|
+
"""Worker function for processing files with Docling in a separate process."""
|
|
62
|
+
# Signal handling for graceful shutdown
|
|
63
|
+
shutdown_requested = False
|
|
64
|
+
|
|
65
|
+
def signal_handler(signum: int, frame) -> None: # noqa: ARG001
|
|
66
|
+
"""Handle shutdown signals gracefully."""
|
|
67
|
+
nonlocal shutdown_requested
|
|
68
|
+
signal_names: dict[int, str] = {signal.SIGTERM: "SIGTERM", signal.SIGINT: "SIGINT"}
|
|
69
|
+
signal_name = signal_names.get(signum, f"signal {signum}")
|
|
70
|
+
|
|
71
|
+
logger.debug(f"Docling worker received {signal_name}, initiating graceful shutdown...")
|
|
72
|
+
shutdown_requested = True
|
|
73
|
+
|
|
74
|
+
# Send shutdown notification to parent process
|
|
75
|
+
with suppress(Exception):
|
|
76
|
+
queue.put({"error": f"Worker interrupted by {signal_name}", "shutdown": True})
|
|
77
|
+
|
|
78
|
+
# Exit gracefully
|
|
79
|
+
sys.exit(0)
|
|
80
|
+
|
|
81
|
+
def check_shutdown() -> None:
|
|
82
|
+
"""Check if shutdown was requested and exit if so."""
|
|
83
|
+
if shutdown_requested:
|
|
84
|
+
logger.info("Shutdown requested, exiting worker...")
|
|
85
|
+
|
|
86
|
+
with suppress(Exception):
|
|
87
|
+
queue.put({"error": "Worker shutdown requested", "shutdown": True})
|
|
88
|
+
|
|
89
|
+
sys.exit(0)
|
|
90
|
+
|
|
91
|
+
# Register signal handlers early
|
|
92
|
+
try:
|
|
93
|
+
signal.signal(signal.SIGTERM, signal_handler)
|
|
94
|
+
signal.signal(signal.SIGINT, signal_handler)
|
|
95
|
+
logger.debug("Signal handlers registered for graceful shutdown")
|
|
96
|
+
except (OSError, ValueError) as e:
|
|
97
|
+
# Some signals might not be available on all platforms
|
|
98
|
+
logger.warning(f"Warning: Could not register signal handlers: {e}")
|
|
99
|
+
|
|
100
|
+
# Check for shutdown before heavy imports
|
|
101
|
+
check_shutdown()
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
from docling.datamodel.base_models import ConversionStatus, InputFormat
|
|
105
|
+
from docling.datamodel.pipeline_options import OcrOptions, PdfPipelineOptions, VlmPipelineOptions
|
|
106
|
+
from docling.document_converter import DocumentConverter, FormatOption, PdfFormatOption
|
|
107
|
+
from docling.models.factories import get_ocr_factory
|
|
108
|
+
from docling.pipeline.vlm_pipeline import VlmPipeline
|
|
109
|
+
|
|
110
|
+
# Check for shutdown after imports
|
|
111
|
+
check_shutdown()
|
|
112
|
+
logger.debug("Docling dependencies loaded successfully")
|
|
113
|
+
|
|
114
|
+
except ModuleNotFoundError:
|
|
115
|
+
msg = (
|
|
116
|
+
"Docling is an optional dependency of Langflow. "
|
|
117
|
+
"Install with `uv pip install 'langflow[docling]'` "
|
|
118
|
+
"or refer to the documentation"
|
|
119
|
+
)
|
|
120
|
+
queue.put({"error": msg})
|
|
121
|
+
return
|
|
122
|
+
except ImportError as e:
|
|
123
|
+
# A different import failed (e.g., a transitive dependency); preserve details.
|
|
124
|
+
queue.put({"error": f"Failed to import a Docling dependency: {e}"})
|
|
125
|
+
return
|
|
126
|
+
except KeyboardInterrupt:
|
|
127
|
+
logger.warning("KeyboardInterrupt during imports, exiting...")
|
|
128
|
+
queue.put({"error": "Worker interrupted during imports", "shutdown": True})
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
# Configure the standard PDF pipeline
|
|
132
|
+
def _get_standard_opts() -> PdfPipelineOptions:
|
|
133
|
+
check_shutdown() # Check before heavy operations
|
|
134
|
+
|
|
135
|
+
pipeline_options = PdfPipelineOptions()
|
|
136
|
+
pipeline_options.do_ocr = ocr_engine != ""
|
|
137
|
+
if pipeline_options.do_ocr:
|
|
138
|
+
ocr_factory = get_ocr_factory(
|
|
139
|
+
allow_external_plugins=False,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
ocr_options: OcrOptions = ocr_factory.create_options(
|
|
143
|
+
kind=ocr_engine,
|
|
144
|
+
)
|
|
145
|
+
pipeline_options.ocr_options = ocr_options
|
|
146
|
+
return pipeline_options
|
|
147
|
+
|
|
148
|
+
# Configure the VLM pipeline
|
|
149
|
+
def _get_vlm_opts() -> VlmPipelineOptions:
|
|
150
|
+
check_shutdown() # Check before heavy operations
|
|
151
|
+
return VlmPipelineOptions()
|
|
152
|
+
|
|
153
|
+
# Configure the main format options and create the DocumentConverter()
|
|
154
|
+
def _get_converter() -> DocumentConverter:
|
|
155
|
+
check_shutdown() # Check before heavy operations
|
|
156
|
+
|
|
157
|
+
if pipeline == "standard":
|
|
158
|
+
pdf_format_option = PdfFormatOption(
|
|
159
|
+
pipeline_options=_get_standard_opts(),
|
|
160
|
+
)
|
|
161
|
+
elif pipeline == "vlm":
|
|
162
|
+
pdf_format_option = PdfFormatOption(pipeline_cls=VlmPipeline, pipeline_options=_get_vlm_opts())
|
|
163
|
+
else:
|
|
164
|
+
msg = f"Unknown pipeline: {pipeline!r}"
|
|
165
|
+
raise ValueError(msg)
|
|
166
|
+
|
|
167
|
+
format_options: dict[InputFormat, FormatOption] = {
|
|
168
|
+
InputFormat.PDF: pdf_format_option,
|
|
169
|
+
InputFormat.IMAGE: pdf_format_option,
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return DocumentConverter(format_options=format_options)
|
|
173
|
+
|
|
174
|
+
try:
|
|
175
|
+
# Check for shutdown before creating converter (can be slow)
|
|
176
|
+
check_shutdown()
|
|
177
|
+
logger.info(f"Initializing {pipeline} pipeline with OCR: {ocr_engine or 'disabled'}")
|
|
178
|
+
|
|
179
|
+
converter = _get_converter()
|
|
180
|
+
|
|
181
|
+
# Check for shutdown before processing files
|
|
182
|
+
check_shutdown()
|
|
183
|
+
logger.info(f"Starting to process {len(file_paths)} files...")
|
|
184
|
+
|
|
185
|
+
# Process files with periodic shutdown checks
|
|
186
|
+
results = []
|
|
187
|
+
for i, file_path in enumerate(file_paths):
|
|
188
|
+
# Check for shutdown before processing each file
|
|
189
|
+
check_shutdown()
|
|
190
|
+
|
|
191
|
+
logger.debug(f"Processing file {i + 1}/{len(file_paths)}: {file_path}")
|
|
192
|
+
|
|
193
|
+
try:
|
|
194
|
+
# Process single file (we can't easily interrupt convert_all)
|
|
195
|
+
single_result = converter.convert_all([file_path])
|
|
196
|
+
results.extend(single_result)
|
|
197
|
+
|
|
198
|
+
# Check for shutdown after each file
|
|
199
|
+
check_shutdown()
|
|
200
|
+
|
|
201
|
+
except (OSError, ValueError, RuntimeError, ImportError) as file_error:
|
|
202
|
+
# Handle specific file processing errors
|
|
203
|
+
logger.error(f"Error processing file {file_path}: {file_error}")
|
|
204
|
+
# Continue with other files, but check for shutdown
|
|
205
|
+
check_shutdown()
|
|
206
|
+
except Exception as file_error: # noqa: BLE001
|
|
207
|
+
# Catch any other unexpected errors to prevent worker crash
|
|
208
|
+
logger.error(f"Unexpected error processing file {file_path}: {file_error}")
|
|
209
|
+
# Continue with other files, but check for shutdown
|
|
210
|
+
check_shutdown()
|
|
211
|
+
|
|
212
|
+
# Final shutdown check before sending results
|
|
213
|
+
check_shutdown()
|
|
214
|
+
|
|
215
|
+
# Process the results while maintaining the original structure
|
|
216
|
+
processed_data = [
|
|
217
|
+
{"document": res.document, "file_path": str(res.input.file), "status": res.status.name}
|
|
218
|
+
if res.status == ConversionStatus.SUCCESS
|
|
219
|
+
else None
|
|
220
|
+
for res in results
|
|
221
|
+
]
|
|
222
|
+
|
|
223
|
+
logger.info(f"Successfully processed {len([d for d in processed_data if d])} files")
|
|
224
|
+
queue.put(processed_data)
|
|
225
|
+
|
|
226
|
+
except KeyboardInterrupt:
|
|
227
|
+
logger.warning("KeyboardInterrupt during processing, exiting gracefully...")
|
|
228
|
+
queue.put({"error": "Worker interrupted during processing", "shutdown": True})
|
|
229
|
+
return
|
|
230
|
+
except Exception as e: # noqa: BLE001
|
|
231
|
+
if shutdown_requested:
|
|
232
|
+
logger.exception("Exception occurred during shutdown, exiting...")
|
|
233
|
+
return
|
|
234
|
+
|
|
235
|
+
# Send any processing error to the main process with traceback
|
|
236
|
+
error_info = {"error": str(e), "traceback": traceback.format_exc()}
|
|
237
|
+
logger.error(f"Error in worker: {error_info}")
|
|
238
|
+
queue.put(error_info)
|
|
239
|
+
finally:
|
|
240
|
+
logger.info("Docling worker finishing...")
|
|
241
|
+
# Ensure we don't leave any hanging processes
|
|
242
|
+
if shutdown_requested:
|
|
243
|
+
logger.debug("Worker shutdown completed")
|
|
244
|
+
else:
|
|
245
|
+
logger.debug("Worker completed normally")
|
langflow/base/data/kb_utils.py
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import math
|
|
2
2
|
from collections import Counter
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from uuid import UUID
|
|
5
|
+
|
|
6
|
+
from langflow.services.database.models.user.crud import get_user_by_id
|
|
7
|
+
from langflow.services.deps import session_scope
|
|
3
8
|
|
|
4
9
|
|
|
5
10
|
def compute_tfidf(documents: list[str], query_terms: list[str]) -> list[float]:
|
|
@@ -102,3 +107,31 @@ def compute_bm25(documents: list[str], query_terms: list[str], k1: float = 1.2,
|
|
|
102
107
|
scores.append(doc_score)
|
|
103
108
|
|
|
104
109
|
return scores
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
async def get_knowledge_bases(kb_root: Path, user_id: UUID | str) -> list[str]:
|
|
113
|
+
"""Retrieve a list of available knowledge bases.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
A list of knowledge base names.
|
|
117
|
+
"""
|
|
118
|
+
if not kb_root.exists():
|
|
119
|
+
return []
|
|
120
|
+
|
|
121
|
+
# Get the current user
|
|
122
|
+
async with session_scope() as db:
|
|
123
|
+
if not user_id:
|
|
124
|
+
msg = "User ID is required for fetching knowledge bases."
|
|
125
|
+
raise ValueError(msg)
|
|
126
|
+
user_id = UUID(user_id) if isinstance(user_id, str) else user_id
|
|
127
|
+
current_user = await get_user_by_id(db, user_id)
|
|
128
|
+
if not current_user:
|
|
129
|
+
msg = f"User with ID {user_id} not found."
|
|
130
|
+
raise ValueError(msg)
|
|
131
|
+
kb_user = current_user.username
|
|
132
|
+
kb_path = kb_root / kb_user
|
|
133
|
+
|
|
134
|
+
if not kb_path.exists():
|
|
135
|
+
return []
|
|
136
|
+
|
|
137
|
+
return [str(d.name) for d in kb_path.iterdir() if not d.name.startswith(".") and d.is_dir()]
|
|
@@ -2,10 +2,10 @@ import concurrent.futures
|
|
|
2
2
|
import json
|
|
3
3
|
|
|
4
4
|
import httpx
|
|
5
|
-
from loguru import logger
|
|
6
5
|
from pydantic import BaseModel, SecretStr
|
|
7
6
|
|
|
8
7
|
from langflow.field_typing import Embeddings
|
|
8
|
+
from langflow.logging.logger import logger
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class AIMLEmbeddingsImpl(BaseModel, Embeddings):
|
langflow/base/io/__init__.py
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
# noqa: A005
|
langflow/base/langwatch/utils.py
CHANGED
langflow/base/mcp/util.py
CHANGED
|
@@ -15,12 +15,12 @@ import httpx
|
|
|
15
15
|
from anyio import ClosedResourceError
|
|
16
16
|
from httpx import codes as httpx_codes
|
|
17
17
|
from langchain_core.tools import StructuredTool
|
|
18
|
-
from loguru import logger
|
|
19
18
|
from mcp import ClientSession
|
|
20
19
|
from mcp.shared.exceptions import McpError
|
|
21
20
|
from pydantic import BaseModel, Field, create_model
|
|
22
21
|
from sqlmodel import select
|
|
23
22
|
|
|
23
|
+
from langflow.logging.logger import logger
|
|
24
24
|
from langflow.services.database.models.flow.model import Flow
|
|
25
25
|
from langflow.services.deps import get_settings_service
|
|
26
26
|
|
|
@@ -214,7 +214,7 @@ def create_tool_coroutine(tool_name: str, arg_schema: type[BaseModel], client) -
|
|
|
214
214
|
try:
|
|
215
215
|
return await client.run_tool(tool_name, arguments=validated.model_dump())
|
|
216
216
|
except Exception as e:
|
|
217
|
-
logger.
|
|
217
|
+
await logger.aerror(f"Tool '{tool_name}' execution failed: {e}")
|
|
218
218
|
# Re-raise with more context
|
|
219
219
|
msg = f"Tool '{tool_name}' execution failed: {e}"
|
|
220
220
|
raise ValueError(msg) from e
|
|
@@ -264,7 +264,7 @@ def get_unique_name(base_name, max_length, existing_names):
|
|
|
264
264
|
i += 1
|
|
265
265
|
|
|
266
266
|
|
|
267
|
-
async def get_flow_snake_case(flow_name: str, user_id: str, session, is_action: bool | None = None) -> Flow | None:
|
|
267
|
+
async def get_flow_snake_case(flow_name: str, user_id: str, session, *, is_action: bool | None = None) -> Flow | None:
|
|
268
268
|
uuid_user_id = UUID(user_id) if isinstance(user_id, str) else user_id
|
|
269
269
|
stmt = select(Flow).where(Flow.user_id == uuid_user_id).where(Flow.is_component == False) # noqa: E712
|
|
270
270
|
flows = (await session.exec(stmt)).all()
|
|
@@ -506,7 +506,7 @@ class MCPSessionManager:
|
|
|
506
506
|
break
|
|
507
507
|
except (RuntimeError, KeyError, ClosedResourceError, ValueError, asyncio.TimeoutError) as e:
|
|
508
508
|
# Handle common recoverable errors without stopping the cleanup loop
|
|
509
|
-
logger.
|
|
509
|
+
await logger.awarning(f"Error in periodic cleanup: {e}")
|
|
510
510
|
|
|
511
511
|
async def _cleanup_idle_sessions(self):
|
|
512
512
|
"""Clean up sessions that have been idle for too long."""
|
|
@@ -523,7 +523,7 @@ class MCPSessionManager:
|
|
|
523
523
|
|
|
524
524
|
# Clean up idle sessions
|
|
525
525
|
for session_id in sessions_to_remove:
|
|
526
|
-
logger.
|
|
526
|
+
await logger.ainfo(f"Cleaning up idle session {session_id} for server {server_key}")
|
|
527
527
|
await self._cleanup_session_by_id(server_key, session_id)
|
|
528
528
|
|
|
529
529
|
# Remove server entry if no sessions left
|
|
@@ -561,7 +561,7 @@ class MCPSessionManager:
|
|
|
561
561
|
# Use a shorter timeout for the connectivity test to fail fast
|
|
562
562
|
response = await asyncio.wait_for(session.list_tools(), timeout=3.0)
|
|
563
563
|
except (asyncio.TimeoutError, ConnectionError, OSError, ValueError) as e:
|
|
564
|
-
logger.
|
|
564
|
+
await logger.adebug(f"Session connectivity test failed (standard error): {e}")
|
|
565
565
|
return False
|
|
566
566
|
except Exception as e:
|
|
567
567
|
# Handle MCP-specific errors that might not be in the standard list
|
|
@@ -574,27 +574,27 @@ class MCPSessionManager:
|
|
|
574
574
|
or "Transport closed" in error_str
|
|
575
575
|
or "Stream closed" in error_str
|
|
576
576
|
):
|
|
577
|
-
logger.
|
|
577
|
+
await logger.adebug(f"Session connectivity test failed (MCP connection error): {e}")
|
|
578
578
|
return False
|
|
579
579
|
# Re-raise unexpected errors
|
|
580
|
-
logger.
|
|
580
|
+
await logger.awarning(f"Unexpected error in connectivity test: {e}")
|
|
581
581
|
raise
|
|
582
582
|
else:
|
|
583
583
|
# Validate that we got a meaningful response
|
|
584
584
|
if response is None:
|
|
585
|
-
logger.
|
|
585
|
+
await logger.adebug("Session connectivity test failed: received None response")
|
|
586
586
|
return False
|
|
587
587
|
try:
|
|
588
588
|
# Check if we can access the tools list (even if empty)
|
|
589
589
|
tools = getattr(response, "tools", None)
|
|
590
590
|
if tools is None:
|
|
591
|
-
logger.
|
|
591
|
+
await logger.adebug("Session connectivity test failed: no tools attribute in response")
|
|
592
592
|
return False
|
|
593
593
|
except (AttributeError, TypeError) as e:
|
|
594
|
-
logger.
|
|
594
|
+
await logger.adebug(f"Session connectivity test failed while validating response: {e}")
|
|
595
595
|
return False
|
|
596
596
|
else:
|
|
597
|
-
logger.
|
|
597
|
+
await logger.adebug(f"Session connectivity test passed: found {len(tools)} tools")
|
|
598
598
|
return True
|
|
599
599
|
|
|
600
600
|
async def get_session(self, context_id: str, connection_params, transport_type: str):
|
|
@@ -625,32 +625,32 @@ class MCPSessionManager:
|
|
|
625
625
|
|
|
626
626
|
# Quick health check
|
|
627
627
|
if await self._validate_session_connectivity(session):
|
|
628
|
-
logger.
|
|
628
|
+
await logger.adebug(f"Reusing existing session {session_id} for server {server_key}")
|
|
629
629
|
# record mapping & bump ref-count for backwards compatibility
|
|
630
630
|
self._context_to_session[context_id] = (server_key, session_id)
|
|
631
631
|
self._session_refcount[(server_key, session_id)] = (
|
|
632
632
|
self._session_refcount.get((server_key, session_id), 0) + 1
|
|
633
633
|
)
|
|
634
634
|
return session
|
|
635
|
-
logger.
|
|
635
|
+
await logger.ainfo(f"Session {session_id} for server {server_key} failed health check, cleaning up")
|
|
636
636
|
await self._cleanup_session_by_id(server_key, session_id)
|
|
637
637
|
else:
|
|
638
638
|
# Task is done, clean up
|
|
639
|
-
logger.
|
|
639
|
+
await logger.ainfo(f"Session {session_id} for server {server_key} task is done, cleaning up")
|
|
640
640
|
await self._cleanup_session_by_id(server_key, session_id)
|
|
641
641
|
|
|
642
642
|
# Check if we've reached the maximum number of sessions for this server
|
|
643
643
|
if len(sessions) >= MAX_SESSIONS_PER_SERVER:
|
|
644
644
|
# Remove the oldest session
|
|
645
645
|
oldest_session_id = min(sessions.keys(), key=lambda x: sessions[x]["last_used"])
|
|
646
|
-
logger.
|
|
646
|
+
await logger.ainfo(
|
|
647
647
|
f"Maximum sessions reached for server {server_key}, removing oldest session {oldest_session_id}"
|
|
648
648
|
)
|
|
649
649
|
await self._cleanup_session_by_id(server_key, oldest_session_id)
|
|
650
650
|
|
|
651
651
|
# Create new session
|
|
652
652
|
session_id = f"{server_key}_{len(sessions)}"
|
|
653
|
-
logger.
|
|
653
|
+
await logger.ainfo(f"Creating new session {session_id} for server {server_key}")
|
|
654
654
|
|
|
655
655
|
if transport_type == "stdio":
|
|
656
656
|
session, task = await self._create_stdio_session(session_id, connection_params)
|
|
@@ -700,7 +700,7 @@ class MCPSessionManager:
|
|
|
700
700
|
try:
|
|
701
701
|
await event.wait()
|
|
702
702
|
except asyncio.CancelledError:
|
|
703
|
-
logger.
|
|
703
|
+
await logger.ainfo(f"Session {session_id} is shutting down")
|
|
704
704
|
except Exception as e: # noqa: BLE001
|
|
705
705
|
if not session_future.done():
|
|
706
706
|
session_future.set_exception(e)
|
|
@@ -723,7 +723,7 @@ class MCPSessionManager:
|
|
|
723
723
|
await task
|
|
724
724
|
self._background_tasks.discard(task)
|
|
725
725
|
msg = f"Timeout waiting for STDIO session {session_id} to initialize"
|
|
726
|
-
logger.
|
|
726
|
+
await logger.aerror(msg)
|
|
727
727
|
raise ValueError(msg) from timeout_err
|
|
728
728
|
|
|
729
729
|
return session, task
|
|
@@ -759,7 +759,7 @@ class MCPSessionManager:
|
|
|
759
759
|
try:
|
|
760
760
|
await event.wait()
|
|
761
761
|
except asyncio.CancelledError:
|
|
762
|
-
logger.
|
|
762
|
+
await logger.ainfo(f"Session {session_id} is shutting down")
|
|
763
763
|
except Exception as e: # noqa: BLE001
|
|
764
764
|
if not session_future.done():
|
|
765
765
|
session_future.set_exception(e)
|
|
@@ -782,7 +782,7 @@ class MCPSessionManager:
|
|
|
782
782
|
await task
|
|
783
783
|
self._background_tasks.discard(task)
|
|
784
784
|
msg = f"Timeout waiting for SSE session {session_id} to initialize"
|
|
785
|
-
logger.
|
|
785
|
+
await logger.aerror(msg)
|
|
786
786
|
raise ValueError(msg) from timeout_err
|
|
787
787
|
|
|
788
788
|
return session, task
|
|
@@ -813,9 +813,9 @@ class MCPSessionManager:
|
|
|
813
813
|
if hasattr(session, "aclose"):
|
|
814
814
|
try:
|
|
815
815
|
await session.aclose()
|
|
816
|
-
logger.
|
|
816
|
+
await logger.adebug("Successfully closed session %s using aclose()", session_id)
|
|
817
817
|
except Exception as e: # noqa: BLE001
|
|
818
|
-
logger.
|
|
818
|
+
await logger.adebug("Error closing session %s with aclose(): %s", session_id, e)
|
|
819
819
|
|
|
820
820
|
# If no aclose, try regular close method
|
|
821
821
|
elif hasattr(session, "close"):
|
|
@@ -824,18 +824,20 @@ class MCPSessionManager:
|
|
|
824
824
|
if inspect.iscoroutinefunction(session.close):
|
|
825
825
|
# It's an async method
|
|
826
826
|
await session.close()
|
|
827
|
-
logger.
|
|
827
|
+
await logger.adebug("Successfully closed session %s using async close()", session_id)
|
|
828
828
|
else:
|
|
829
829
|
# Try calling it and check if result is awaitable
|
|
830
830
|
close_result = session.close()
|
|
831
831
|
if inspect.isawaitable(close_result):
|
|
832
832
|
await close_result
|
|
833
|
-
logger.
|
|
833
|
+
await logger.adebug(
|
|
834
|
+
"Successfully closed session %s using awaitable close()", session_id
|
|
835
|
+
)
|
|
834
836
|
else:
|
|
835
837
|
# It's a synchronous close
|
|
836
|
-
logger.
|
|
838
|
+
await logger.adebug("Successfully closed session %s using sync close()", session_id)
|
|
837
839
|
except Exception as e: # noqa: BLE001
|
|
838
|
-
logger.
|
|
840
|
+
await logger.adebug("Error closing session %s with close(): %s", session_id, e)
|
|
839
841
|
|
|
840
842
|
# Cancel the background task which will properly close the session
|
|
841
843
|
if "task" in session_info:
|
|
@@ -845,9 +847,9 @@ class MCPSessionManager:
|
|
|
845
847
|
try:
|
|
846
848
|
await task
|
|
847
849
|
except asyncio.CancelledError:
|
|
848
|
-
logger.
|
|
850
|
+
await logger.ainfo(f"Cancelled task for session {session_id}")
|
|
849
851
|
except Exception as e: # noqa: BLE001
|
|
850
|
-
logger.
|
|
852
|
+
await logger.awarning(f"Error cleaning up session {session_id}: {e}")
|
|
851
853
|
finally:
|
|
852
854
|
# Remove from sessions dict
|
|
853
855
|
del sessions[session_id]
|
|
@@ -900,7 +902,7 @@ class MCPSessionManager:
|
|
|
900
902
|
"""
|
|
901
903
|
mapping = self._context_to_session.get(context_id)
|
|
902
904
|
if not mapping:
|
|
903
|
-
logger.
|
|
905
|
+
await logger.adebug(f"No session mapping found for context_id {context_id}")
|
|
904
906
|
return
|
|
905
907
|
|
|
906
908
|
server_key, session_id = mapping
|
|
@@ -1031,7 +1033,7 @@ class MCPStdioClient:
|
|
|
1031
1033
|
|
|
1032
1034
|
for attempt in range(max_retries):
|
|
1033
1035
|
try:
|
|
1034
|
-
logger.
|
|
1036
|
+
await logger.adebug(f"Attempting to run tool '{tool_name}' (attempt {attempt + 1}/{max_retries})")
|
|
1035
1037
|
# Get or create persistent session
|
|
1036
1038
|
session = await self._get_or_create_session()
|
|
1037
1039
|
|
|
@@ -1041,7 +1043,7 @@ class MCPStdioClient:
|
|
|
1041
1043
|
)
|
|
1042
1044
|
except Exception as e:
|
|
1043
1045
|
current_error_type = type(e).__name__
|
|
1044
|
-
logger.
|
|
1046
|
+
await logger.awarning(f"Tool '{tool_name}' failed on attempt {attempt + 1}: {current_error_type} - {e}")
|
|
1045
1047
|
|
|
1046
1048
|
# Import specific MCP error types for detection
|
|
1047
1049
|
try:
|
|
@@ -1056,14 +1058,14 @@ class MCPStdioClient:
|
|
|
1056
1058
|
|
|
1057
1059
|
# If we're getting the same error type repeatedly, don't retry
|
|
1058
1060
|
if last_error_type == current_error_type and attempt > 0:
|
|
1059
|
-
logger.
|
|
1061
|
+
await logger.aerror(f"Repeated {current_error_type} error for tool '{tool_name}', not retrying")
|
|
1060
1062
|
break
|
|
1061
1063
|
|
|
1062
1064
|
last_error_type = current_error_type
|
|
1063
1065
|
|
|
1064
1066
|
# If it's a connection error (ClosedResourceError or MCP connection closed) and we have retries left
|
|
1065
1067
|
if (is_closed_resource_error or is_mcp_connection_error) and attempt < max_retries - 1:
|
|
1066
|
-
logger.
|
|
1068
|
+
await logger.awarning(
|
|
1067
1069
|
f"MCP session connection issue for tool '{tool_name}', retrying with fresh session..."
|
|
1068
1070
|
)
|
|
1069
1071
|
# Clean up the dead session
|
|
@@ -1076,7 +1078,7 @@ class MCPStdioClient:
|
|
|
1076
1078
|
|
|
1077
1079
|
# If it's a timeout error and we have retries left, try once more
|
|
1078
1080
|
if is_timeout_error and attempt < max_retries - 1:
|
|
1079
|
-
logger.
|
|
1081
|
+
await logger.awarning(f"Tool '{tool_name}' timed out, retrying...")
|
|
1080
1082
|
# Don't clean up session for timeouts, might just be a slow response
|
|
1081
1083
|
await asyncio.sleep(1.0)
|
|
1082
1084
|
continue
|
|
@@ -1089,7 +1091,7 @@ class MCPStdioClient:
|
|
|
1089
1091
|
or is_timeout_error
|
|
1090
1092
|
):
|
|
1091
1093
|
msg = f"Failed to run tool '{tool_name}' after {attempt + 1} attempts: {e}"
|
|
1092
|
-
logger.
|
|
1094
|
+
await logger.aerror(msg)
|
|
1093
1095
|
# Clean up failed session from cache
|
|
1094
1096
|
if self._session_context and self._component_cache:
|
|
1095
1097
|
cache_key = f"mcp_session_stdio_{self._session_context}"
|
|
@@ -1099,12 +1101,12 @@ class MCPStdioClient:
|
|
|
1099
1101
|
# Re-raise unexpected errors
|
|
1100
1102
|
raise
|
|
1101
1103
|
else:
|
|
1102
|
-
logger.
|
|
1104
|
+
await logger.adebug(f"Tool '{tool_name}' completed successfully")
|
|
1103
1105
|
return result
|
|
1104
1106
|
|
|
1105
1107
|
# This should never be reached due to the exception handling above
|
|
1106
1108
|
msg = f"Failed to run tool '{tool_name}': Maximum retries exceeded with repeated {last_error_type} errors"
|
|
1107
|
-
logger.
|
|
1109
|
+
await logger.aerror(msg)
|
|
1108
1110
|
raise ValueError(msg)
|
|
1109
1111
|
|
|
1110
1112
|
async def disconnect(self):
|
|
@@ -1213,7 +1215,7 @@ class MCPSseClient:
|
|
|
1213
1215
|
return response.headers.get("Location", url)
|
|
1214
1216
|
# Don't treat 404 as an error here - let the main connection handle it
|
|
1215
1217
|
except (httpx.RequestError, httpx.HTTPError) as e:
|
|
1216
|
-
logger.
|
|
1218
|
+
await logger.awarning(f"Error checking redirects: {e}")
|
|
1217
1219
|
return url
|
|
1218
1220
|
|
|
1219
1221
|
async def _connect_to_server(
|
|
@@ -1336,7 +1338,7 @@ class MCPSseClient:
|
|
|
1336
1338
|
|
|
1337
1339
|
for attempt in range(max_retries):
|
|
1338
1340
|
try:
|
|
1339
|
-
logger.
|
|
1341
|
+
await logger.adebug(f"Attempting to run tool '{tool_name}' (attempt {attempt + 1}/{max_retries})")
|
|
1340
1342
|
# Get or create persistent session
|
|
1341
1343
|
session = await self._get_or_create_session()
|
|
1342
1344
|
|
|
@@ -1349,7 +1351,7 @@ class MCPSseClient:
|
|
|
1349
1351
|
)
|
|
1350
1352
|
except Exception as e:
|
|
1351
1353
|
current_error_type = type(e).__name__
|
|
1352
|
-
logger.
|
|
1354
|
+
await logger.awarning(f"Tool '{tool_name}' failed on attempt {attempt + 1}: {current_error_type} - {e}")
|
|
1353
1355
|
|
|
1354
1356
|
# Import specific MCP error types for detection
|
|
1355
1357
|
try:
|
|
@@ -1367,14 +1369,14 @@ class MCPSseClient:
|
|
|
1367
1369
|
|
|
1368
1370
|
# If we're getting the same error type repeatedly, don't retry
|
|
1369
1371
|
if last_error_type == current_error_type and attempt > 0:
|
|
1370
|
-
logger.
|
|
1372
|
+
await logger.aerror(f"Repeated {current_error_type} error for tool '{tool_name}', not retrying")
|
|
1371
1373
|
break
|
|
1372
1374
|
|
|
1373
1375
|
last_error_type = current_error_type
|
|
1374
1376
|
|
|
1375
1377
|
# If it's a connection error (ClosedResourceError or MCP connection closed) and we have retries left
|
|
1376
1378
|
if (is_closed_resource_error or is_mcp_connection_error) and attempt < max_retries - 1:
|
|
1377
|
-
logger.
|
|
1379
|
+
await logger.awarning(
|
|
1378
1380
|
f"MCP session connection issue for tool '{tool_name}', retrying with fresh session..."
|
|
1379
1381
|
)
|
|
1380
1382
|
# Clean up the dead session
|
|
@@ -1387,7 +1389,7 @@ class MCPSseClient:
|
|
|
1387
1389
|
|
|
1388
1390
|
# If it's a timeout error and we have retries left, try once more
|
|
1389
1391
|
if is_timeout_error and attempt < max_retries - 1:
|
|
1390
|
-
logger.
|
|
1392
|
+
await logger.awarning(f"Tool '{tool_name}' timed out, retrying...")
|
|
1391
1393
|
# Don't clean up session for timeouts, might just be a slow response
|
|
1392
1394
|
await asyncio.sleep(1.0)
|
|
1393
1395
|
continue
|
|
@@ -1400,7 +1402,7 @@ class MCPSseClient:
|
|
|
1400
1402
|
or is_timeout_error
|
|
1401
1403
|
):
|
|
1402
1404
|
msg = f"Failed to run tool '{tool_name}' after {attempt + 1} attempts: {e}"
|
|
1403
|
-
logger.
|
|
1405
|
+
await logger.aerror(msg)
|
|
1404
1406
|
# Clean up failed session from cache
|
|
1405
1407
|
if self._session_context and self._component_cache:
|
|
1406
1408
|
cache_key = f"mcp_session_sse_{self._session_context}"
|
|
@@ -1410,12 +1412,12 @@ class MCPSseClient:
|
|
|
1410
1412
|
# Re-raise unexpected errors
|
|
1411
1413
|
raise
|
|
1412
1414
|
else:
|
|
1413
|
-
logger.
|
|
1415
|
+
await logger.adebug(f"Tool '{tool_name}' completed successfully")
|
|
1414
1416
|
return result
|
|
1415
1417
|
|
|
1416
1418
|
# This should never be reached due to the exception handling above
|
|
1417
1419
|
msg = f"Failed to run tool '{tool_name}': Maximum retries exceeded with repeated {last_error_type} errors"
|
|
1418
|
-
logger.
|
|
1420
|
+
await logger.aerror(msg)
|
|
1419
1421
|
raise ValueError(msg)
|
|
1420
1422
|
|
|
1421
1423
|
async def disconnect(self):
|
langflow/base/models/model.py
CHANGED
|
@@ -252,7 +252,7 @@ class LCModelComponent(Component):
|
|
|
252
252
|
if stream:
|
|
253
253
|
lf_message, result = await self._handle_stream(runnable, inputs)
|
|
254
254
|
else:
|
|
255
|
-
message = runnable.
|
|
255
|
+
message = await runnable.ainvoke(inputs)
|
|
256
256
|
result = message.content if hasattr(message, "content") else message
|
|
257
257
|
if isinstance(message, AIMessage):
|
|
258
258
|
status_message = self.build_status_message(message)
|
|
@@ -288,7 +288,7 @@ class LCModelComponent(Component):
|
|
|
288
288
|
else:
|
|
289
289
|
session_id = None
|
|
290
290
|
model_message = Message(
|
|
291
|
-
text=runnable.
|
|
291
|
+
text=runnable.astream(inputs),
|
|
292
292
|
sender=MESSAGE_SENDER_AI,
|
|
293
293
|
sender_name="AI",
|
|
294
294
|
properties={"icon": self.icon, "state": "partial"},
|
|
@@ -298,7 +298,7 @@ class LCModelComponent(Component):
|
|
|
298
298
|
lf_message = await self.send_message(model_message)
|
|
299
299
|
result = lf_message.text
|
|
300
300
|
else:
|
|
301
|
-
message = runnable.
|
|
301
|
+
message = await runnable.ainvoke(inputs)
|
|
302
302
|
result = message.content if hasattr(message, "content") else message
|
|
303
303
|
return lf_message, result
|
|
304
304
|
|