langflow-base-nightly 0.5.0.dev35__py3-none-any.whl → 0.5.0.dev37__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 (157) hide show
  1. langflow/api/v1/knowledge_bases.py +16 -9
  2. langflow/api/v2/files.py +3 -1
  3. langflow/base/data/kb_utils.py +33 -0
  4. langflow/base/models/model.py +3 -3
  5. langflow/components/agents/mcp_component.py +40 -55
  6. langflow/components/data/kb_ingest.py +116 -43
  7. langflow/components/data/kb_retrieval.py +24 -26
  8. langflow/components/docling/__init__.py +198 -0
  9. langflow/components/docling/docling_inline.py +102 -60
  10. langflow/components/processing/save_file.py +6 -32
  11. langflow/components/vectorstores/astradb.py +30 -19
  12. langflow/frontend/assets/{SlackIcon-B260Qg_R.js → SlackIcon-CnvyOamQ.js} +1 -1
  13. langflow/frontend/assets/{Wikipedia-BB2mbgyd.js → Wikipedia-nyTEXdr2.js} +1 -1
  14. langflow/frontend/assets/{Wolfram-DytXC9hF.js → Wolfram-BYMQkNSq.js} +1 -1
  15. langflow/frontend/assets/{index-BdIWbCEL.js → index-8WdfSTTz.js} +1 -1
  16. langflow/frontend/assets/{index-D87Zw62M.js → index-8yMsjVV2.js} +1 -1
  17. langflow/frontend/assets/{index-DyJDHm2D.js → index-B1YN7oMV.js} +1 -1
  18. langflow/frontend/assets/{index-BEDxAk3N.js → index-B3Sur4Z3.js} +1 -1
  19. langflow/frontend/assets/{index-DhzEUXfr.js → index-B748uLP1.js} +1 -1
  20. langflow/frontend/assets/{index-4eRtaV45.js → index-BB15_iOb.js} +1 -1
  21. langflow/frontend/assets/{index-C_1RBTul.js → index-BBxAPk1y.js} +1 -1
  22. langflow/frontend/assets/{index-DHlEwAxb.js → index-BCCGvqay.js} +1 -1
  23. langflow/frontend/assets/{index-ci4XHjbJ.js → index-BChjg6Az.js} +3 -3
  24. langflow/frontend/assets/{index-B9Mo3ndZ.js → index-BEMw2Np8.js} +1 -1
  25. langflow/frontend/assets/{index-BKvKC-12.js → index-BFp_O-c9.js} +1 -1
  26. langflow/frontend/assets/{index-Ym6gz0T6.js → index-BIQQCMvz.js} +1 -1
  27. langflow/frontend/assets/{index-CwIxqYlT.js → index-BIXaW2aY.js} +1 -1
  28. langflow/frontend/assets/{index-BxkZkBgQ.js → index-BIzTEqFh.js} +1 -1
  29. langflow/frontend/assets/{index-C76aBV_h.js → index-BLGYN-9b.js} +1 -1
  30. langflow/frontend/assets/{index-B-c82Fnu.js → index-BOB_zsjl.js} +1 -1
  31. langflow/frontend/assets/{index-BbsND1Qg.js → index-BOeo01QB.js} +1 -1
  32. langflow/frontend/assets/{index-DztLFiip.js → index-BQ6NUdMY.js} +1 -1
  33. langflow/frontend/assets/{index-G_U_kPAd.js → index-BR0bkVqX.js} +1 -1
  34. langflow/frontend/assets/{index-R7q8cAek.js → index-BRYjyhAd.js} +1 -1
  35. langflow/frontend/assets/{index-Ccb5B8zG.js → index-BRxvproo.js} +1 -1
  36. langflow/frontend/assets/{index-B8y58M9b.js → index-BTEW9e8P.js} +1 -1
  37. langflow/frontend/assets/{index-DdzVmJHE.js → index-BTrsh9LS.js} +1 -1
  38. langflow/frontend/assets/{index-CkSzjCqM.js → index-BVEZDXxS.js} +1 -1
  39. langflow/frontend/assets/{index-BLROcaSz.js → index-BWmPX4iQ.js} +1 -1
  40. langflow/frontend/assets/{index-Ct9_T9ox.js → index-BX5D-USa.js} +1 -1
  41. langflow/frontend/assets/{index-DtJyCbzF.js → index-BZgXW854.js} +1 -1
  42. langflow/frontend/assets/{index-CkQ-bJ4G.js → index-BbJjt5m4.js} +1 -1
  43. langflow/frontend/assets/{index-D5PeCofu.js → index-BbRm7beF.js} +1 -1
  44. langflow/frontend/assets/{index-Uq2ij_SS.js → index-Bd6WtbKA.js} +1 -1
  45. langflow/frontend/assets/{index-sS6XLk3j.js → index-BhIOhlCH.js} +1 -1
  46. langflow/frontend/assets/{index-dkS0ek2S.js → index-BkPYpfgw.js} +1 -1
  47. langflow/frontend/assets/{index-BOYTBrh9.js → index-BmX5CoED.js} +1 -1
  48. langflow/frontend/assets/{index-CMGZGIx_.js → index-Bnqod3vk.js} +1 -1
  49. langflow/frontend/assets/{index-Bisa4IQF.js → index-Boso-xEw.js} +1 -1
  50. langflow/frontend/assets/{index-DqSH4x-R.js → index-BqPpO6KG.js} +1 -1
  51. langflow/frontend/assets/{index-B5ed-sAv.js → index-Bsa0xZyL.js} +1 -1
  52. langflow/frontend/assets/{index-CoUlHbtg.js → index-Bv8h2Z-q.js} +1 -1
  53. langflow/frontend/assets/{index-tOy_uloT.js → index-BvT7L317.js} +1 -1
  54. langflow/frontend/assets/{index-D-zkHcob.js → index-BvwZfF2i.js} +1 -1
  55. langflow/frontend/assets/{index-DxIs8VSp.js → index-Bvxg4_ux.js} +1 -1
  56. langflow/frontend/assets/{index-CqDUqHfd.js → index-BxEuHa76.js} +1 -1
  57. langflow/frontend/assets/{index-DX7XsAcx.js → index-BzEUlaw_.js} +1 -1
  58. langflow/frontend/assets/{index-BCK-ZyIh.js → index-BzL_EoKd.js} +1 -1
  59. langflow/frontend/assets/{index-BNbWMmAV.js → index-C-2hghRJ.js} +1 -1
  60. langflow/frontend/assets/{index-CWWo2zOA.js → index-C26RqKWL.js} +1 -1
  61. langflow/frontend/assets/{index-BcgB3rXH.js → index-C6jri9Wm.js} +1 -1
  62. langflow/frontend/assets/{index-mBjJYD9q.js → index-C7QWbnLK.js} +1 -1
  63. langflow/frontend/assets/{index-D0HmkH0H.js → index-C82JjCPD.js} +1 -1
  64. langflow/frontend/assets/{index-Cpgkb0Q3.js → index-CCePCqkT.js} +1 -1
  65. langflow/frontend/assets/{index-IFGgPiye.js → index-CCxGSSTT.js} +1 -1
  66. langflow/frontend/assets/{index-BOEf7-ty.js → index-CFDvOtKC.js} +1 -1
  67. langflow/frontend/assets/{index-Ba3RTMXI.js → index-CJo_cyWW.js} +1 -1
  68. langflow/frontend/assets/{index-Cx__T92e.js → index-CLPdN-q6.js} +1 -1
  69. langflow/frontend/assets/{index-CF4dtI6S.js → index-CQMoqLAu.js} +1 -1
  70. langflow/frontend/assets/{index-3qMh9x6K.js → index-CTrt1Q_j.js} +1 -1
  71. langflow/frontend/assets/{index-rcdQpNcU.js → index-CVQmT7ZL.js} +1 -1
  72. langflow/frontend/assets/{index-Dpz3oBf5.js → index-CWdkbVsd.js} +1 -1
  73. langflow/frontend/assets/{index-D0s9f6Re.js → index-CYDAYm-i.js} +1 -1
  74. langflow/frontend/assets/{index-BjENqyKe.js → index-CYe8Ipef.js} +1 -1
  75. langflow/frontend/assets/{index-ByFXr9Iq.js → index-CZQ9rXNa.js} +1 -1
  76. langflow/frontend/assets/{index-VcXZzovW.js → index-C_TdzfAn.js} +1 -1
  77. langflow/frontend/assets/{index-LrMzDsq9.js → index-C_veJlEb.js} +1 -1
  78. langflow/frontend/assets/{index-BdYgKk1d.js → index-CaQ_H9ww.js} +1 -1
  79. langflow/frontend/assets/{index-CHFO5O4g.js → index-Car-zdor.js} +1 -1
  80. langflow/frontend/assets/{index-DZzbmg3J.js → index-ChXJpBz4.js} +1 -1
  81. langflow/frontend/assets/{index-C7x9R_Yo.js → index-CmplyEaa.js} +1 -1
  82. langflow/frontend/assets/{index-Cd5zuUUK.js → index-CpcbQZIF.js} +1 -1
  83. langflow/frontend/assets/{index-D9eflZfP.js → index-CpvYQ0ug.js} +1 -1
  84. langflow/frontend/assets/{index-DS1EgA10.js → index-CvcEzq4x.js} +1 -1
  85. langflow/frontend/assets/{index-hOkEW3JP.js → index-CxvP91st.js} +1 -1
  86. langflow/frontend/assets/{index-DasrI03Y.js → index-CyPvTB63.js} +1 -1
  87. langflow/frontend/assets/{index-BJrY2Fiu.js → index-D-9TI74R.js} +1 -1
  88. langflow/frontend/assets/{index-BlBl2tvQ.js → index-D3DDfngy.js} +1 -1
  89. langflow/frontend/assets/{index-DzeIsaBm.js → index-D5_DsUJc.js} +1 -1
  90. langflow/frontend/assets/{index-AY5Dm2mG.js → index-D6PSjHxP.js} +1 -1
  91. langflow/frontend/assets/{index-C9N80hP8.js → index-D8GJngXa.js} +1 -1
  92. langflow/frontend/assets/{index-BxWXWRmZ.js → index-D8lOi1GI.js} +1 -1
  93. langflow/frontend/assets/{index-DWkMJnbd.js → index-DCRk27Tp.js} +1 -1
  94. langflow/frontend/assets/{index-BnLT29qW.js → index-DF5VwgU6.js} +1 -1
  95. langflow/frontend/assets/{index-7xXgqu09.js → index-DGRMNe9n.js} +1 -1
  96. langflow/frontend/assets/{index-3TJWUdmx.js → index-DHq8TQPB.js} +1 -1
  97. langflow/frontend/assets/{index-BVtf6m9S.js → index-DIDDfmlJ.js} +1 -1
  98. langflow/frontend/assets/{index-B2ggrBuR.js → index-DIkNW9Cd.js} +1 -1
  99. langflow/frontend/assets/{index-r1LZg-PY.js → index-DJB12jIC.js} +1 -1
  100. langflow/frontend/assets/{index-DS9I4y48.js → index-DK1Ptcc4.js} +1 -1
  101. langflow/frontend/assets/{index-CG7cp0nD.js → index-DKHNourL.js} +1 -1
  102. langflow/frontend/assets/{index-BeNby7qF.js → index-DPCzHdsC.js} +1 -1
  103. langflow/frontend/assets/{index-COL0eiWI.js → index-DVlceYFD.js} +1 -1
  104. langflow/frontend/assets/{index-DK8vNpXK.js → index-DZTC5pdT.js} +1 -1
  105. langflow/frontend/assets/{index-Baka5dKE.js → index-Db71w3lq.js} +1 -1
  106. langflow/frontend/assets/{index-Du9aJK7m.js → index-DbMFlnHE.js} +1 -1
  107. langflow/frontend/assets/{index-CvQ0w8Pj.js → index-DfngcQxO.js} +1 -1
  108. langflow/frontend/assets/{index-DIqSyDVO.js → index-DfxYyS3M.js} +1 -1
  109. langflow/frontend/assets/{index-3uOAA_XX.js → index-Dg-63Si_.js} +1 -1
  110. langflow/frontend/assets/{index-BsBWP-Dh.js → index-DjQETUy8.js} +1 -1
  111. langflow/frontend/assets/{index-CDFLVFB4.js → index-DkXy1WFo.js} +1 -1
  112. langflow/frontend/assets/{index-B8TlNgn-.js → index-DkelbYy7.js} +1 -1
  113. langflow/frontend/assets/{index-GODbXlHC.js → index-DmMDPoi0.js} +1 -1
  114. langflow/frontend/assets/{index-DpQKtcXu.js → index-DnEGCgih.js} +1 -1
  115. langflow/frontend/assets/{index-VHmUHUUU.js → index-DpClkXIV.js} +1 -1
  116. langflow/frontend/assets/{index-BRWNIt9F.js → index-Dq5ilsem.js} +1 -1
  117. langflow/frontend/assets/{index-DDNNv4C0.js → index-Dqd4RjYA.js} +1 -1
  118. langflow/frontend/assets/{index-C2Xd7UkR.js → index-Dsps-jKu.js} +1 -1
  119. langflow/frontend/assets/{index-BVHvIhT5.js → index-Du_18NCU.js} +1 -1
  120. langflow/frontend/assets/{index-C7V5U9yH.js → index-DysKpOuj.js} +1 -1
  121. langflow/frontend/assets/{index-Bxml6wXu.js → index-DytJENYD.js} +1 -1
  122. langflow/frontend/assets/{index-BWq9GTzt.js → index-DzW2mfkK.js} +1 -1
  123. langflow/frontend/assets/{index-js8ceOaP.js → index-FUxmznS-.js} +1 -1
  124. langflow/frontend/assets/{index-DuAeoC-H.js → index-Gkrq-vzm.js} +1 -1
  125. langflow/frontend/assets/{index-DPX6X_bw.js → index-HK3bVMYA.js} +1 -1
  126. langflow/frontend/assets/{index-BEKoRwsX.js → index-LbYjHKkn.js} +1 -1
  127. langflow/frontend/assets/{index-C8KD3LPb.js → index-OazXJdEl.js} +1 -1
  128. langflow/frontend/assets/{index-DpJiH-Rk.js → index-Q9vDw0Xl.js} +1 -1
  129. langflow/frontend/assets/{index-DWr_zPkx.js → index-Ui4xUImO.js} +1 -1
  130. langflow/frontend/assets/{index-BejHxU5W.js → index-WPFivmdQ.js} +1 -1
  131. langflow/frontend/assets/{index-lKEJpUsF.js → index-_UcqeEjm.js} +1 -1
  132. langflow/frontend/assets/{index-VZnN0P6C.js → index-ajRge-Mg.js} +1 -1
  133. langflow/frontend/assets/{index-BQB-iDYl.js → index-cvZdgWHQ.js} +1 -1
  134. langflow/frontend/assets/{index-AlJ7td-D.js → index-dcnYpT9N.js} +1 -1
  135. langflow/frontend/assets/{index-DKEXZFUO.js → index-l7bzB8Ex.js} +1 -1
  136. langflow/frontend/assets/index-nVwHLjuV.js +1 -0
  137. langflow/frontend/assets/{index-BtJ2o21k.js → index-pCQ_yw8m.js} +1 -1
  138. langflow/frontend/assets/{index-B536IPXH.js → index-rXV1G1aB.js} +1 -1
  139. langflow/frontend/assets/{index-BIkqesA-.js → index-tVYiABdp.js} +1 -1
  140. langflow/frontend/assets/{index-CJwYfDBz.js → index-xuIrH2Dq.js} +1 -1
  141. langflow/frontend/assets/{index-BXMhmvTj.js → index-yCHsaqs8.js} +1 -1
  142. langflow/frontend/assets/{index-BqUeOc7Y.js → index-ya2uXE8v.js} +1 -1
  143. langflow/frontend/assets/lazyIconImports-t6wEndt1.js +2 -0
  144. langflow/frontend/assets/{use-post-add-user-HN0rRnhv.js → use-post-add-user-BrBYH9eR.js} +1 -1
  145. langflow/frontend/index.html +1 -1
  146. langflow/initial_setup/starter_projects/Hybrid Search RAG.json +2 -2
  147. langflow/initial_setup/starter_projects/Knowledge Ingestion.json +2 -2
  148. langflow/initial_setup/starter_projects/Knowledge Retrieval.json +2 -2
  149. langflow/initial_setup/starter_projects/News Aggregator.json +2 -19
  150. langflow/initial_setup/starter_projects/Nvidia Remix.json +2 -19
  151. langflow/initial_setup/starter_projects/Vector Store RAG.json +4 -4
  152. langflow/processing/process.py +1 -1
  153. {langflow_base_nightly-0.5.0.dev35.dist-info → langflow_base_nightly-0.5.0.dev37.dist-info}/METADATA +1 -1
  154. {langflow_base_nightly-0.5.0.dev35.dist-info → langflow_base_nightly-0.5.0.dev37.dist-info}/RECORD +156 -155
  155. langflow/frontend/assets/lazyIconImports-Bh1TFfvH.js +0 -2
  156. {langflow_base_nightly-0.5.0.dev35.dist-info → langflow_base_nightly-0.5.0.dev37.dist-info}/WHEEL +0 -0
  157. {langflow_base_nightly-0.5.0.dev35.dist-info → langflow_base_nightly-0.5.0.dev37.dist-info}/entry_points.txt +0 -0
@@ -9,6 +9,7 @@ from langchain_chroma import Chroma
9
9
  from loguru import logger
10
10
  from pydantic import BaseModel
11
11
 
12
+ from langflow.api.utils import CurrentActiveUser
12
13
  from langflow.services.deps import get_settings_service
13
14
 
14
15
  router = APIRouter(tags=["Knowledge Bases"], prefix="/knowledge_bases")
@@ -290,17 +291,19 @@ def get_kb_metadata(kb_path: Path) -> dict:
290
291
 
291
292
  @router.get("", status_code=HTTPStatus.OK)
292
293
  @router.get("/", status_code=HTTPStatus.OK)
293
- async def list_knowledge_bases() -> list[KnowledgeBaseInfo]:
294
+ async def list_knowledge_bases(current_user: CurrentActiveUser) -> list[KnowledgeBaseInfo]:
294
295
  """List all available knowledge bases."""
295
296
  try:
296
297
  kb_root_path = get_kb_root_path()
298
+ kb_user = current_user.username
299
+ kb_path = kb_root_path / kb_user
297
300
 
298
- if not kb_root_path.exists():
301
+ if not kb_path.exists():
299
302
  return []
300
303
 
301
304
  knowledge_bases = []
302
305
 
303
- for kb_dir in kb_root_path.iterdir():
306
+ for kb_dir in kb_path.iterdir():
304
307
  if not kb_dir.is_dir() or kb_dir.name.startswith("."):
305
308
  continue
306
309
 
@@ -340,11 +343,12 @@ async def list_knowledge_bases() -> list[KnowledgeBaseInfo]:
340
343
 
341
344
 
342
345
  @router.get("/{kb_name}", status_code=HTTPStatus.OK)
343
- async def get_knowledge_base(kb_name: str) -> KnowledgeBaseInfo:
346
+ async def get_knowledge_base(kb_name: str, current_user: CurrentActiveUser) -> KnowledgeBaseInfo:
344
347
  """Get detailed information about a specific knowledge base."""
345
348
  try:
346
349
  kb_root_path = get_kb_root_path()
347
- kb_path = kb_root_path / kb_name
350
+ kb_user = current_user.username
351
+ kb_path = kb_root_path / kb_user / kb_name
348
352
 
349
353
  if not kb_path.exists() or not kb_path.is_dir():
350
354
  raise HTTPException(status_code=404, detail=f"Knowledge base '{kb_name}' not found")
@@ -374,11 +378,12 @@ async def get_knowledge_base(kb_name: str) -> KnowledgeBaseInfo:
374
378
 
375
379
 
376
380
  @router.delete("/{kb_name}", status_code=HTTPStatus.OK)
377
- async def delete_knowledge_base(kb_name: str) -> dict[str, str]:
381
+ async def delete_knowledge_base(kb_name: str, current_user: CurrentActiveUser) -> dict[str, str]:
378
382
  """Delete a specific knowledge base."""
379
383
  try:
380
384
  kb_root_path = get_kb_root_path()
381
- kb_path = kb_root_path / kb_name
385
+ kb_user = current_user.username
386
+ kb_path = kb_root_path / kb_user / kb_name
382
387
 
383
388
  if not kb_path.exists() or not kb_path.is_dir():
384
389
  raise HTTPException(status_code=404, detail=f"Knowledge base '{kb_name}' not found")
@@ -396,15 +401,17 @@ async def delete_knowledge_base(kb_name: str) -> dict[str, str]:
396
401
 
397
402
  @router.delete("", status_code=HTTPStatus.OK)
398
403
  @router.delete("/", status_code=HTTPStatus.OK)
399
- async def delete_knowledge_bases_bulk(request: BulkDeleteRequest) -> dict[str, object]:
404
+ async def delete_knowledge_bases_bulk(request: BulkDeleteRequest, current_user: CurrentActiveUser) -> dict[str, object]:
400
405
  """Delete multiple knowledge bases."""
401
406
  try:
402
407
  kb_root_path = get_kb_root_path()
408
+ kb_user = current_user.username
409
+ kb_user_path = kb_root_path / kb_user
403
410
  deleted_count = 0
404
411
  not_found_kbs = []
405
412
 
406
413
  for kb_name in request.kb_names:
407
- kb_path = kb_root_path / kb_name
414
+ kb_path = kb_user_path / kb_name
408
415
 
409
416
  if not kb_path.exists() or not kb_path.is_dir():
410
417
  not_found_kbs.append(kb_name)
langflow/api/v2/files.py CHANGED
@@ -123,7 +123,9 @@ async def upload_user_file(
123
123
  unique_filename = new_filename
124
124
  else:
125
125
  # For normal files, ensure unique name by appending a count if necessary
126
- stmt = select(UserFile).where(col(UserFile.name).like(f"{root_filename}%"))
126
+ stmt = select(UserFile).where(
127
+ col(UserFile.name).like(f"{root_filename}%"), UserFile.user_id == current_user.id
128
+ )
127
129
  existing_files = await session.exec(stmt)
128
130
  files = existing_files.all() # Fetch all matching records
129
131
 
@@ -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()]
@@ -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.invoke(inputs)
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.stream(inputs),
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.invoke(inputs)
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
 
@@ -16,16 +16,15 @@ from langflow.base.mcp.util import (
16
16
  )
17
17
  from langflow.custom.custom_component.component_with_cache import ComponentWithCache
18
18
  from langflow.inputs.inputs import InputTypes # noqa: TC001
19
- from langflow.io import DropdownInput, McpInput, MessageTextInput, Output, SecretStrInput
19
+ from langflow.io import DropdownInput, McpInput, MessageTextInput, Output
20
20
  from langflow.io.schema import flatten_schema, schema_to_langflow_inputs
21
21
  from langflow.logging import logger
22
22
  from langflow.schema.dataframe import DataFrame
23
23
  from langflow.schema.message import Message
24
24
 
25
25
  # Import get_server from the backend API
26
- from langflow.services.auth.utils import create_user_longterm_token, get_current_user
27
26
  from langflow.services.database.models.user.crud import get_user_by_id
28
- from langflow.services.deps import get_session, get_settings_service, get_storage_service
27
+ from langflow.services.deps import get_settings_service, get_storage_service, session_scope
29
28
 
30
29
 
31
30
  class MCPToolsComponent(ComponentWithCache):
@@ -96,13 +95,6 @@ class MCPToolsComponent(ComponentWithCache):
96
95
  show=False,
97
96
  tool_mode=False,
98
97
  ),
99
- SecretStrInput(
100
- name="api_key",
101
- display_name="Langflow API Key",
102
- info="Langflow API key for authentication when fetching MCP servers and tools.",
103
- required=False,
104
- advanced=True,
105
- ),
106
98
  ]
107
99
 
108
100
  outputs = [
@@ -161,19 +153,11 @@ class MCPToolsComponent(ComponentWithCache):
161
153
  return self.tools, {"name": server_name, "config": server_config_from_value}
162
154
 
163
155
  try:
164
- async for db in get_session():
165
- # TODO: In 1.6, this may need to be removed or adjusted
166
- # Try to get the super user token, if possible
167
- if self.api_key:
168
- current_user = await get_current_user(
169
- token=None,
170
- query_param=self.api_key,
171
- header_param=None,
172
- db=db,
173
- )
174
- else:
175
- user_id, _ = await create_user_longterm_token(db)
176
- current_user = await get_user_by_id(db, user_id)
156
+ async with session_scope() as db:
157
+ if not self.user_id:
158
+ msg = "User ID is required for fetching MCP tools."
159
+ raise ValueError(msg)
160
+ current_user = await get_user_by_id(db, self.user_id)
177
161
 
178
162
  # Try to get server config from DB/API
179
163
  server_config = await get_server(
@@ -184,39 +168,38 @@ class MCPToolsComponent(ComponentWithCache):
184
168
  settings_service=get_settings_service(),
185
169
  )
186
170
 
187
- # If get_server returns empty but we have a config, use it
188
- if not server_config and server_config_from_value:
189
- server_config = server_config_from_value
190
-
191
- if not server_config:
192
- self.tools = []
193
- return [], {"name": server_name, "config": server_config}
171
+ # If get_server returns empty but we have a config, use it
172
+ if not server_config and server_config_from_value:
173
+ server_config = server_config_from_value
174
+
175
+ if not server_config:
176
+ self.tools = []
177
+ return [], {"name": server_name, "config": server_config}
178
+
179
+ _, tool_list, tool_cache = await update_tools(
180
+ server_name=server_name,
181
+ server_config=server_config,
182
+ mcp_stdio_client=self.stdio_client,
183
+ mcp_sse_client=self.sse_client,
184
+ )
185
+
186
+ self.tool_names = [tool.name for tool in tool_list if hasattr(tool, "name")]
187
+ self._tool_cache = tool_cache
188
+ self.tools = tool_list
189
+ # Cache the result using shared cache
190
+ cache_data = {
191
+ "tools": tool_list,
192
+ "tool_names": self.tool_names,
193
+ "tool_cache": tool_cache,
194
+ "config": server_config,
195
+ }
196
+
197
+ # Safely update the servers cache
198
+ current_servers_cache = safe_cache_get(self._shared_component_cache, "servers", {})
199
+ if isinstance(current_servers_cache, dict):
200
+ current_servers_cache[server_name] = cache_data
201
+ safe_cache_set(self._shared_component_cache, "servers", current_servers_cache)
194
202
 
195
- _, tool_list, tool_cache = await update_tools(
196
- server_name=server_name,
197
- server_config=server_config,
198
- mcp_stdio_client=self.stdio_client,
199
- mcp_sse_client=self.sse_client,
200
- )
201
-
202
- self.tool_names = [tool.name for tool in tool_list if hasattr(tool, "name")]
203
- self._tool_cache = tool_cache
204
- self.tools = tool_list
205
- # Cache the result using shared cache
206
- cache_data = {
207
- "tools": tool_list,
208
- "tool_names": self.tool_names,
209
- "tool_cache": tool_cache,
210
- "config": server_config,
211
- }
212
-
213
- # Safely update the servers cache
214
- current_servers_cache = safe_cache_get(self._shared_component_cache, "servers", {})
215
- if isinstance(current_servers_cache, dict):
216
- current_servers_cache[server_name] = cache_data
217
- safe_cache_set(self._shared_component_cache, "servers", current_servers_cache)
218
-
219
- return tool_list, {"name": server_name, "config": server_config}
220
203
  except (TimeoutError, asyncio.TimeoutError) as e:
221
204
  msg = f"Timeout updating tool list: {e!s}"
222
205
  logger.exception(msg)
@@ -225,6 +208,8 @@ class MCPToolsComponent(ComponentWithCache):
225
208
  msg = f"Error updating tool list: {e!s}"
226
209
  logger.exception(msg)
227
210
  raise ValueError(msg) from e
211
+ else:
212
+ return tool_list, {"name": server_name, "config": server_config}
228
213
 
229
214
  async def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None) -> dict:
230
215
  """Toggle the visibility of connection-specific fields based on the selected mode."""
@@ -1,5 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import asyncio
4
+ import contextlib
3
5
  import hashlib
4
6
  import json
5
7
  import re
@@ -14,6 +16,7 @@ from cryptography.fernet import InvalidToken
14
16
  from langchain_chroma import Chroma
15
17
  from loguru import logger
16
18
 
19
+ from langflow.base.data.kb_utils import get_knowledge_bases
17
20
  from langflow.base.models.openai_constants import OPENAI_EMBEDDING_MODEL_NAMES
18
21
  from langflow.custom import Component
19
22
  from langflow.io import BoolInput, DataFrameInput, DropdownInput, IntInput, Output, SecretStrInput, StrInput, TableInput
@@ -21,7 +24,8 @@ from langflow.schema.data import Data
21
24
  from langflow.schema.dotdict import dotdict # noqa: TC001
22
25
  from langflow.schema.table import EditMode
23
26
  from langflow.services.auth.utils import decrypt_api_key, encrypt_api_key
24
- from langflow.services.deps import get_settings_service
27
+ from langflow.services.database.models.user.crud import get_user_by_id
28
+ from langflow.services.deps import get_settings_service, get_variable_service, session_scope
25
29
 
26
30
  HUGGINGFACE_MODEL_NAMES = ["sentence-transformers/all-MiniLM-L6-v2", "sentence-transformers/all-mpnet-base-v2"]
27
31
  COHERE_MODEL_NAMES = ["embed-english-v3.0", "embed-multilingual-v3.0"]
@@ -43,6 +47,10 @@ class KBIngestionComponent(Component):
43
47
  icon = "database"
44
48
  name = "KBIngestion"
45
49
 
50
+ def __init__(self, *args, **kwargs) -> None:
51
+ super().__init__(*args, **kwargs)
52
+ self._cached_kb_path: Path | None = None
53
+
46
54
  @dataclass
47
55
  class NewKnowledgeBaseInput:
48
56
  functionality: str = "create"
@@ -76,7 +84,7 @@ class KBIngestionComponent(Component):
76
84
  display_name="API Key",
77
85
  info="Provider API key for embedding model",
78
86
  required=True,
79
- load_from_db=True,
87
+ load_from_db=False,
80
88
  ),
81
89
  },
82
90
  },
@@ -91,11 +99,7 @@ class KBIngestionComponent(Component):
91
99
  display_name="Knowledge",
92
100
  info="Select the knowledge to load data from.",
93
101
  required=True,
94
- options=[
95
- str(d.name) for d in KNOWLEDGE_BASES_ROOT_PATH.iterdir() if not d.name.startswith(".") and d.is_dir()
96
- ]
97
- if KNOWLEDGE_BASES_ROOT_PATH.exists()
98
- else [],
102
+ options=[],
99
103
  refresh_button=True,
100
104
  dialog_inputs=asdict(NewKnowledgeBaseInput()),
101
105
  ),
@@ -329,22 +333,23 @@ class KBIngestionComponent(Component):
329
333
 
330
334
  return metadata
331
335
 
332
- def _create_vector_store(
336
+ async def _create_vector_store(
333
337
  self, df_source: pd.DataFrame, config_list: list[dict[str, Any]], embedding_model: str, api_key: str
334
338
  ) -> None:
335
339
  """Create vector store following Local DB component pattern."""
336
340
  try:
337
341
  # Set up vector store directory
338
- base_dir = self._get_kb_root()
339
-
340
- vector_store_dir = base_dir / self.knowledge_base
342
+ vector_store_dir = await self._kb_path()
343
+ if not vector_store_dir:
344
+ msg = "Knowledge base path is not set. Please create a new knowledge base first."
345
+ raise ValueError(msg)
341
346
  vector_store_dir.mkdir(parents=True, exist_ok=True)
342
347
 
343
348
  # Create embeddings model
344
349
  embedding_function = self._build_embeddings(embedding_model, api_key)
345
350
 
346
351
  # Convert DataFrame to Data objects (following Local DB pattern)
347
- data_objects = self._convert_df_to_data_objects(df_source, config_list)
352
+ data_objects = await self._convert_df_to_data_objects(df_source, config_list)
348
353
 
349
354
  # Create vector store
350
355
  chroma = Chroma(
@@ -367,16 +372,18 @@ class KBIngestionComponent(Component):
367
372
  except (OSError, ValueError, RuntimeError) as e:
368
373
  self.log(f"Error creating vector store: {e}")
369
374
 
370
- def _convert_df_to_data_objects(self, df_source: pd.DataFrame, config_list: list[dict[str, Any]]) -> list[Data]:
375
+ async def _convert_df_to_data_objects(
376
+ self, df_source: pd.DataFrame, config_list: list[dict[str, Any]]
377
+ ) -> list[Data]:
371
378
  """Convert DataFrame to Data objects for vector store."""
372
379
  data_objects: list[Data] = []
373
380
 
374
381
  # Set up vector store directory
375
- base_dir = self._get_kb_root()
382
+ kb_path = await self._kb_path()
376
383
 
377
384
  # If we don't allow duplicates, we need to get the existing hashes
378
385
  chroma = Chroma(
379
- persist_directory=str(base_dir / self.knowledge_base),
386
+ persist_directory=str(kb_path),
380
387
  collection_name=self.knowledge_base,
381
388
  )
382
389
 
@@ -466,10 +473,34 @@ class KBIngestionComponent(Component):
466
473
  # Check allowed characters (condition 3)
467
474
  return re.match(r"^[a-zA-Z0-9_-]+$", name) is not None
468
475
 
476
+ async def _kb_path(self) -> Path | None:
477
+ # Check if we already have the path cached
478
+ cached_path = getattr(self, "_cached_kb_path", None)
479
+ if cached_path is not None:
480
+ return cached_path
481
+
482
+ # If not cached, compute it
483
+ async with session_scope() as db:
484
+ if not self.user_id:
485
+ msg = "User ID is required for fetching knowledge base path."
486
+ raise ValueError(msg)
487
+ current_user = await get_user_by_id(db, self.user_id)
488
+ if not current_user:
489
+ msg = f"User with ID {self.user_id} not found."
490
+ raise ValueError(msg)
491
+ kb_user = current_user.username
492
+
493
+ kb_root = self._get_kb_root()
494
+
495
+ # Cache the result
496
+ self._cached_kb_path = kb_root / kb_user / self.knowledge_base
497
+
498
+ return self._cached_kb_path
499
+
469
500
  # ---------------------------------------------------------------------
470
501
  # OUTPUT METHODS
471
502
  # ---------------------------------------------------------------------
472
- def build_kb_info(self) -> Data:
503
+ async def build_kb_info(self) -> Data:
473
504
  """Main ingestion routine → returns a dict with KB metadata."""
474
505
  try:
475
506
  # Get source DataFrame
@@ -479,11 +510,11 @@ class KBIngestionComponent(Component):
479
510
  config_list = self._validate_column_config(df_source)
480
511
  column_metadata = self._build_column_metadata(config_list, df_source)
481
512
 
482
- # Prepare KB folder (using File Component patterns)
483
- kb_root = self._get_kb_root()
484
- kb_path = kb_root / self.knowledge_base
485
-
486
513
  # Read the embedding info from the knowledge base folder
514
+ kb_path = await self._kb_path()
515
+ if not kb_path:
516
+ msg = "Knowledge base path is not set. Please create a new knowledge base first."
517
+ raise ValueError(msg)
487
518
  metadata_path = kb_path / "embedding_metadata.json"
488
519
 
489
520
  # If the API key is not provided, try to read it from the metadata file
@@ -506,7 +537,7 @@ class KBIngestionComponent(Component):
506
537
  )
507
538
 
508
539
  # Create vector store following Local DB component pattern
509
- self._create_vector_store(df_source, config_list, embedding_model=embedding_model, api_key=api_key)
540
+ await self._create_vector_store(df_source, config_list, embedding_model=embedding_model, api_key=api_key)
510
541
 
511
542
  # Save KB files (using File Component storage patterns)
512
543
  self._save_kb_files(kb_path, config_list)
@@ -532,40 +563,77 @@ class KBIngestionComponent(Component):
532
563
  self.status = f"❌ KB ingestion failed: {e}"
533
564
  return Data(data={"error": str(e), "kb_name": self.knowledge_base})
534
565
 
535
- def _get_knowledge_bases(self) -> list[str]:
536
- """Retrieve a list of available knowledge bases.
537
-
538
- Returns:
539
- A list of knowledge base names.
540
- """
541
- # Return the list of directories in the knowledge base root path
542
- kb_root_path = self._get_kb_root()
543
-
544
- if not kb_root_path.exists():
545
- return []
546
-
547
- return [str(d.name) for d in kb_root_path.iterdir() if not d.name.startswith(".") and d.is_dir()]
566
+ async def _get_api_key_variable(self, field_value: dict[str, Any]):
567
+ async with session_scope() as db:
568
+ if not self.user_id:
569
+ msg = "User ID is required for fetching global variables."
570
+ raise ValueError(msg)
571
+ current_user = await get_user_by_id(db, self.user_id)
572
+ if not current_user:
573
+ msg = f"User with ID {self.user_id} not found."
574
+ raise ValueError(msg)
575
+ variable_service = get_variable_service()
576
+
577
+ # Process the api_key field variable
578
+ return await variable_service.get_variable(
579
+ user_id=current_user.id,
580
+ name=field_value["03_api_key"],
581
+ field="",
582
+ session=db,
583
+ )
548
584
 
549
- def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None) -> dotdict:
585
+ async def update_build_config(
586
+ self,
587
+ build_config: dotdict,
588
+ field_value: Any,
589
+ field_name: str | None = None,
590
+ ) -> dotdict:
550
591
  """Update build configuration based on provider selection."""
551
592
  # Create a new knowledge base
552
593
  if field_name == "knowledge_base":
594
+ async with session_scope() as db:
595
+ if not self.user_id:
596
+ msg = "User ID is required for fetching knowledge base list."
597
+ raise ValueError(msg)
598
+ current_user = await get_user_by_id(db, self.user_id)
599
+ if not current_user:
600
+ msg = f"User with ID {self.user_id} not found."
601
+ raise ValueError(msg)
602
+ kb_user = current_user.username
553
603
  if isinstance(field_value, dict) and "01_new_kb_name" in field_value:
554
604
  # Validate the knowledge base name - Make sure it follows these rules:
555
605
  if not self.is_valid_collection_name(field_value["01_new_kb_name"]):
556
606
  msg = f"Invalid knowledge base name: {field_value['01_new_kb_name']}"
557
607
  raise ValueError(msg)
558
608
 
609
+ api_key = field_value.get("03_api_key", None)
610
+ with contextlib.suppress(Exception):
611
+ # If the API key is a variable, resolve it
612
+ api_key = await self._get_api_key_variable(field_value)
613
+
614
+ # Make sure api_key is a string
615
+ if not isinstance(api_key, str):
616
+ msg = "API key must be a string."
617
+ raise ValueError(msg)
618
+
559
619
  # We need to test the API Key one time against the embedding model
560
- embed_model = self._build_embeddings(
561
- embedding_model=field_value["02_embedding_model"], api_key=field_value["03_api_key"]
562
- )
620
+ embed_model = self._build_embeddings(embedding_model=field_value["02_embedding_model"], api_key=api_key)
563
621
 
564
- # Try to generate a dummy embedding to validate the API key
565
- embed_model.embed_query("test")
622
+ # Try to generate a dummy embedding to validate the API key without blocking the event loop
623
+ try:
624
+ await asyncio.wait_for(
625
+ asyncio.to_thread(embed_model.embed_query, "test"),
626
+ timeout=10,
627
+ )
628
+ except TimeoutError as e:
629
+ msg = "Embedding validation timed out. Please verify network connectivity and key."
630
+ raise ValueError(msg) from e
631
+ except Exception as e:
632
+ msg = f"Embedding validation failed: {e!s}"
633
+ raise ValueError(msg) from e
566
634
 
567
635
  # Create the new knowledge base directory
568
- kb_path = KNOWLEDGE_BASES_ROOT_PATH / field_value["01_new_kb_name"]
636
+ kb_path = KNOWLEDGE_BASES_ROOT_PATH / kb_user / field_value["01_new_kb_name"]
569
637
  kb_path.mkdir(parents=True, exist_ok=True)
570
638
 
571
639
  # Save the embedding metadata
@@ -573,11 +641,16 @@ class KBIngestionComponent(Component):
573
641
  self._save_embedding_metadata(
574
642
  kb_path=kb_path,
575
643
  embedding_model=field_value["02_embedding_model"],
576
- api_key=field_value["03_api_key"],
644
+ api_key=api_key,
577
645
  )
578
646
 
579
647
  # Update the knowledge base options dynamically
580
- build_config["knowledge_base"]["options"] = self._get_knowledge_bases()
648
+ build_config["knowledge_base"]["options"] = await get_knowledge_bases(
649
+ KNOWLEDGE_BASES_ROOT_PATH,
650
+ user_id=self.user_id,
651
+ )
652
+
653
+ # If the selected knowledge base is not available, reset it
581
654
  if build_config["knowledge_base"]["value"] not in build_config["knowledge_base"]["options"]:
582
655
  build_config["knowledge_base"]["value"] = None
583
656