OpenRAG 0.4.0__tar.gz → 0.4.0.dev2__tar.gz

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 (164) hide show
  1. {openrag-0.4.0/src/openrag.egg-info → openrag-0.4.0.dev2}/PKG-INFO +2 -4
  2. {openrag-0.4.0 → openrag-0.4.0.dev2}/pyproject.toml +3 -5
  3. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/documents.py +8 -15
  4. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/settings.py +61 -150
  5. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/config/settings.py +5 -2
  6. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/main.py +17 -46
  7. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/models/processors.py +23 -52
  8. {openrag-0.4.0 → openrag-0.4.0.dev2/src/openrag.egg-info}/PKG-INFO +2 -4
  9. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/openrag.egg-info/requires.txt +1 -3
  10. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/services/flows_service.py +36 -110
  11. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/services/search_service.py +9 -70
  12. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/services/task_service.py +24 -33
  13. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/_assets/docker-compose.yml +1 -3
  14. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/_assets/flows/ingestion_flow.json +2 -2
  15. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/_assets/flows/openrag_agent.json +2 -2
  16. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/_assets/flows/openrag_nudges.json +2 -2
  17. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/_assets/flows/openrag_url_mcp.json +2 -2
  18. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/config_fields.py +0 -6
  19. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/main.py +30 -83
  20. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/managers/container_manager.py +1 -1
  21. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/managers/env_manager.py +0 -5
  22. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/screens/config.py +0 -83
  23. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/screens/monitor.py +6 -25
  24. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/utils/file_utils.py +1 -29
  25. {openrag-0.4.0 → openrag-0.4.0.dev2}/LICENSE +0 -0
  26. {openrag-0.4.0 → openrag-0.4.0.dev2}/MANIFEST.in +0 -0
  27. {openrag-0.4.0 → openrag-0.4.0.dev2}/README.md +0 -0
  28. {openrag-0.4.0 → openrag-0.4.0.dev2}/setup.cfg +0 -0
  29. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/agent.py +0 -0
  30. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/__init__.py +0 -0
  31. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/auth.py +0 -0
  32. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/chat.py +0 -0
  33. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/connector_router.py +0 -0
  34. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/connectors.py +0 -0
  35. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/docling.py +0 -0
  36. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/flows.py +0 -0
  37. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/keys.py +0 -0
  38. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/knowledge_filter.py +0 -0
  39. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/langflow_files.py +0 -0
  40. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/models.py +0 -0
  41. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/nudges.py +0 -0
  42. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/oidc.py +0 -0
  43. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/provider_health.py +0 -0
  44. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/provider_validation.py +0 -0
  45. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/router.py +0 -0
  46. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/search.py +0 -0
  47. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/tasks.py +0 -0
  48. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/upload.py +0 -0
  49. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/v1/__init__.py +0 -0
  50. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/v1/chat.py +0 -0
  51. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/v1/documents.py +0 -0
  52. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/v1/knowledge_filters.py +0 -0
  53. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/v1/models.py +0 -0
  54. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/v1/search.py +0 -0
  55. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/api/v1/settings.py +0 -0
  56. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/auth_context.py +0 -0
  57. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/config/__init__.py +0 -0
  58. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/config/config_manager.py +0 -0
  59. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/config/model_constants.py +0 -0
  60. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/__init__.py +0 -0
  61. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/aws_s3/__init__.py +0 -0
  62. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/aws_s3/api.py +0 -0
  63. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/aws_s3/auth.py +0 -0
  64. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/aws_s3/connector.py +0 -0
  65. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/aws_s3/models.py +0 -0
  66. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/aws_s3/support.py +0 -0
  67. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/base.py +0 -0
  68. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/connection_manager.py +0 -0
  69. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/google_drive/__init__.py +0 -0
  70. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/google_drive/connector.py +0 -0
  71. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/google_drive/oauth.py +0 -0
  72. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/ibm_cos/__init__.py +0 -0
  73. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/ibm_cos/api.py +0 -0
  74. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/ibm_cos/auth.py +0 -0
  75. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/ibm_cos/connector.py +0 -0
  76. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/ibm_cos/models.py +0 -0
  77. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/ibm_cos/support.py +0 -0
  78. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/langflow_connector_service.py +0 -0
  79. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/onedrive/__init__.py +0 -0
  80. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/onedrive/connector.py +0 -0
  81. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/onedrive/oauth.py +0 -0
  82. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/service.py +0 -0
  83. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/sharepoint/__init__.py +0 -0
  84. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/sharepoint/connector.py +0 -0
  85. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/sharepoint/oauth.py +0 -0
  86. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/connectors/sharepoint/utils.py +0 -0
  87. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/dependencies.py +0 -0
  88. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/models/__init__.py +0 -0
  89. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/models/tasks.py +0 -0
  90. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/models/url.py +0 -0
  91. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/openrag.egg-info/SOURCES.txt +0 -0
  92. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/openrag.egg-info/dependency_links.txt +0 -0
  93. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/openrag.egg-info/entry_points.txt +0 -0
  94. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/openrag.egg-info/top_level.txt +0 -0
  95. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/services/__init__.py +0 -0
  96. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/services/api_key_service.py +0 -0
  97. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/services/auth_service.py +0 -0
  98. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/services/chat_service.py +0 -0
  99. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/services/conversation_persistence_service.py +0 -0
  100. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/services/document_service.py +0 -0
  101. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/services/knowledge_filter_service.py +0 -0
  102. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/services/langflow_file_service.py +0 -0
  103. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/services/langflow_history_service.py +0 -0
  104. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/services/langflow_mcp_service.py +0 -0
  105. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/services/models_service.py +0 -0
  106. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/services/monitor_service.py +0 -0
  107. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/services/session_ownership_service.py +0 -0
  108. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/session_manager.py +0 -0
  109. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/__init__.py +0 -0
  110. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/_assets/docker-compose.gpu.yml +0 -0
  111. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/_assets/flows/components/ollama_embedding.json +0 -0
  112. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/_assets/flows/components/ollama_llm.json +0 -0
  113. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/_assets/flows/components/ollama_llm_text.json +0 -0
  114. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/_assets/flows/components/watsonx_embedding.json +0 -0
  115. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/_assets/flows/components/watsonx_llm.json +0 -0
  116. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/_assets/flows/components/watsonx_llm_text.json +0 -0
  117. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/_assets/openrag-documents/docling.pdf +0 -0
  118. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/_assets/openrag-documents/ibm_anthropic.pdf +0 -0
  119. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/_assets/openrag-documents/openrag-documentation.pdf +0 -0
  120. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/_assets/openrag-documents/warmup_ocr.pdf +0 -0
  121. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/cli.py +0 -0
  122. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/managers/__init__.py +0 -0
  123. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/managers/docling_manager.py +0 -0
  124. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/screens/__init__.py +0 -0
  125. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/screens/diagnostics.py +0 -0
  126. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/screens/logs.py +0 -0
  127. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/screens/welcome.py +0 -0
  128. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/utils/__init__.py +0 -0
  129. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/utils/clipboard.py +0 -0
  130. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/utils/platform.py +0 -0
  131. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/utils/startup_checks.py +0 -0
  132. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/utils/validation.py +0 -0
  133. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/utils/version_check.py +0 -0
  134. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/widgets/__init__.py +0 -0
  135. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/widgets/command_modal.py +0 -0
  136. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/widgets/diagnostics_notification.py +0 -0
  137. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/widgets/error_notification.py +0 -0
  138. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/widgets/factory_reset_warning_modal.py +0 -0
  139. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/widgets/flow_backup_warning_modal.py +0 -0
  140. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/widgets/prune_options_modal.py +0 -0
  141. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/widgets/upgrade_instructions_modal.py +0 -0
  142. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/widgets/version_mismatch_warning_modal.py +0 -0
  143. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/tui/widgets/waves.py +0 -0
  144. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/utils/__init__.py +0 -0
  145. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/utils/acl_utils.py +0 -0
  146. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/utils/container_utils.py +0 -0
  147. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/utils/docling_client.py +0 -0
  148. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/utils/document_processing.py +0 -0
  149. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/utils/embedding_fields.py +0 -0
  150. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/utils/embeddings.py +0 -0
  151. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/utils/env_utils.py +0 -0
  152. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/utils/gpu_detection.py +0 -0
  153. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/utils/hash_utils.py +0 -0
  154. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/utils/langflow_headers.py +0 -0
  155. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/utils/langflow_utils.py +0 -0
  156. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/utils/logging_config.py +0 -0
  157. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/utils/opensearch_queries.py +0 -0
  158. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/utils/opensearch_utils.py +0 -0
  159. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/utils/paths.py +0 -0
  160. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/utils/telemetry/__init__.py +0 -0
  161. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/utils/telemetry/category.py +0 -0
  162. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/utils/telemetry/client.py +0 -0
  163. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/utils/telemetry/message_id.py +0 -0
  164. {openrag-0.4.0 → openrag-0.4.0.dev2}/src/utils/version_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openrag
3
- Version: 0.4.0
3
+ Version: 0.4.0.dev2
4
4
  Summary: OpenRAG is a comprehensive Retrieval-Augmented Generation platform that enables intelligent document search and AI-powered conversations.
5
5
  Classifier: Development Status :: 4 - Beta
6
6
  Classifier: Environment :: Console
@@ -15,6 +15,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
15
15
  Requires-Python: >=3.13
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE
18
+ Requires-Dist: agentd>=0.2.2
18
19
  Requires-Dist: aiofiles>=24.1.0
19
20
  Requires-Dist: cryptography>=45.0.6
20
21
  Requires-Dist: google-api-python-client>=2.143.0
@@ -36,9 +37,6 @@ Requires-Dist: python-dotenv>=1.0.0
36
37
  Requires-Dist: textual-fspicker>=0.6.0
37
38
  Requires-Dist: structlog>=25.4.0
38
39
  Requires-Dist: zxcvbn>=4.5.0
39
- Requires-Dist: openai>=1.0.0
40
- Requires-Dist: pyyaml>=6.0
41
- Requires-Dist: tiktoken>=0.7.0
42
40
  Dynamic: license-file
43
41
 
44
42
  <div align="center">
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "openrag"
7
- version = "0.4.0"
7
+ version = "0.4.0.dev2"
8
8
  description = "OpenRAG is a comprehensive Retrieval-Augmented Generation platform that enables intelligent document search and AI-powered conversations."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -21,6 +21,7 @@ classifiers = [
21
21
  "Topic :: Software Development :: Libraries :: Python Modules",
22
22
  ]
23
23
  dependencies = [
24
+ "agentd>=0.2.2",
24
25
  "aiofiles>=24.1.0",
25
26
  "cryptography>=45.0.6",
26
27
  "google-api-python-client>=2.143.0",
@@ -41,10 +42,7 @@ dependencies = [
41
42
  "python-dotenv>=1.0.0",
42
43
  "textual-fspicker>=0.6.0",
43
44
  "structlog>=25.4.0",
44
- "zxcvbn>=4.5.0",
45
- "openai>=1.0.0",
46
- "pyyaml>=6.0",
47
- "tiktoken>=0.7.0",
45
+ "zxcvbn>=4.5.0"
48
46
  ]
49
47
 
50
48
  [dependency-groups]
@@ -121,26 +121,16 @@ async def check_filename_exists(
121
121
  )
122
122
 
123
123
  from utils.opensearch_queries import build_filename_search_body
124
- from utils.file_utils import get_filename_aliases
125
124
 
126
- candidate_filenames = get_filename_aliases(filename)
127
- if not candidate_filenames:
128
- return JSONResponse({"exists": False, "filename": filename}, status_code=200)
125
+ search_body = build_filename_search_body(filename, size=1, source=["filename"])
129
126
 
130
127
  logger.debug("Checking filename existence", filename=filename, index_name=get_index_name())
131
- exists = False
132
128
 
133
129
  try:
134
- for candidate in candidate_filenames:
135
- search_body = build_filename_search_body(candidate, size=1, source=["filename"])
136
- response = await opensearch_client.search(
137
- index=get_index_name(),
138
- body=search_body
139
- )
140
- hits = response.get("hits", {}).get("hits", [])
141
- if hits:
142
- exists = True
143
- break
130
+ response = await opensearch_client.search(
131
+ index=get_index_name(),
132
+ body=search_body
133
+ )
144
134
  except Exception as search_err:
145
135
  if "index_not_found_exception" in str(search_err):
146
136
  logger.info("Index does not exist, creating it now before upload")
@@ -148,6 +138,9 @@ async def check_filename_exists(
148
138
  return JSONResponse({"exists": False, "filename": filename}, status_code=200)
149
139
  raise
150
140
 
141
+ hits = response.get("hits", {}).get("hits", [])
142
+ exists = len(hits) > 0
143
+
151
144
  return JSONResponse({"exists": exists, "filename": filename}, status_code=200)
152
145
 
153
146
  except Exception as e:
@@ -1,4 +1,3 @@
1
- import asyncio
2
1
  import json
3
2
  import platform
4
3
  from fastapi import Depends, Request, HTTPException
@@ -37,7 +36,6 @@ from dependencies import (
37
36
  from session_manager import User
38
37
 
39
38
  logger = get_logger(__name__)
40
- _background_tasks: set[asyncio.Task] = set()
41
39
 
42
40
 
43
41
  class SettingsUpdateBody(BaseModel):
@@ -211,9 +209,6 @@ class RollbackResponse(BaseModel):
211
209
  cancelled_tasks: int
212
210
  deleted_files: int
213
211
 
214
- class RollbackBody(BaseModel):
215
- embedding_only: bool = False
216
-
217
212
 
218
213
  # Docling preset configurations
219
214
  def get_docling_preset_configs(
@@ -610,12 +605,6 @@ async def update_settings(
610
605
  logger.error(f"Failed to update docling settings in flow: {str(e)}")
611
606
 
612
607
  if body.chunk_size is not None:
613
- effective_overlap = body.chunk_overlap if body.chunk_overlap is not None else current_config.knowledge.chunk_overlap
614
- if effective_overlap >= body.chunk_size:
615
- raise HTTPException(
616
- status_code=422,
617
- detail="chunk_overlap must be less than chunk_size"
618
- )
619
608
  current_config.knowledge.chunk_size = body.chunk_size
620
609
  config_updated = True
621
610
  await TelemetryClient.send_event(
@@ -636,12 +625,6 @@ async def update_settings(
636
625
  # The config will still be saved
637
626
 
638
627
  if body.chunk_overlap is not None:
639
- effective_chunk_size = body.chunk_size if body.chunk_size is not None else current_config.knowledge.chunk_size
640
- if body.chunk_overlap >= effective_chunk_size:
641
- raise HTTPException(
642
- status_code=422,
643
- detail="chunk_overlap must be less than chunk_size"
644
- )
645
628
  current_config.knowledge.chunk_overlap = body.chunk_overlap
646
629
  config_updated = True
647
630
  await TelemetryClient.send_event(
@@ -832,31 +815,30 @@ async def update_settings(
832
815
  {"error": "Failed to save configuration"}, status_code=500
833
816
  )
834
817
 
835
- # Refresh patched client immediately so subsequent requests pick up latest config.
818
+ # Update Langflow global variables and model values if provider settings changed
836
819
  await clients.refresh_patched_client()
837
820
 
838
- # Run expensive Langflow sync in the background to keep settings updates responsive.
839
821
  if should_validate or provider_updated:
840
- task = asyncio.create_task(
841
- _run_async_post_save_langflow_updates(
842
- session_manager=session_manager,
843
- update_mcp_servers=(
844
- body.embedding_provider is not None
845
- or body.embedding_model is not None
846
- or provider_updated
847
- ),
848
- update_model_values=(
849
- body.llm_provider is not None
850
- or body.llm_model is not None
851
- or body.embedding_provider is not None
852
- or body.embedding_model is not None
853
- or provider_updated
854
- ),
855
- )
856
- )
857
- # Keep a strong reference until completion to avoid premature GC cancellation.
858
- _background_tasks.add(task)
859
- task.add_done_callback(_background_tasks.discard)
822
+ try:
823
+ flows_service = _get_flows_service()
824
+
825
+ # Update global variables
826
+ await _update_langflow_global_variables(current_config, flows_service=flows_service)
827
+
828
+ # Update LLM client credentials when embedding selection changes
829
+ if body.embedding_provider is not None or body.embedding_model is not None:
830
+ await _update_mcp_servers_with_provider_credentials(
831
+ current_config, session_manager, flows_service=flows_service
832
+ )
833
+
834
+ # Update model values if provider or model changed (including removals that trigger fallback)
835
+ if body.llm_provider is not None or body.llm_model is not None or body.embedding_provider is not None or body.embedding_model is not None or provider_updated:
836
+ await _update_langflow_model_values(current_config, flows_service)
837
+
838
+ except Exception as e:
839
+ logger.error(f"Failed to update Langflow settings: {str(e)}")
840
+ # Don't fail the entire settings update if Langflow update fails
841
+ # The config was still saved
860
842
 
861
843
 
862
844
  set_fields = [k for k, v in body.model_dump().items() if v is not None]
@@ -1398,37 +1380,6 @@ async def _update_langflow_global_variables(config, flows_service=None):
1398
1380
  raise
1399
1381
 
1400
1382
 
1401
- async def _run_async_post_save_langflow_updates(
1402
- session_manager,
1403
- update_mcp_servers: bool,
1404
- update_model_values: bool,
1405
- ) -> None:
1406
- """Apply post-save Langflow synchronization asynchronously."""
1407
- try:
1408
- current_config = get_openrag_config()
1409
- flows_service = _get_flows_service()
1410
-
1411
- # Update global variables
1412
- await _update_langflow_global_variables(
1413
- current_config, flows_service=flows_service
1414
- )
1415
-
1416
- # Update LLM client credentials when embedding selection changes
1417
- if update_mcp_servers:
1418
- await _update_mcp_servers_with_provider_credentials(
1419
- current_config, session_manager, flows_service=flows_service
1420
- )
1421
-
1422
- # Update model values if provider/model changed (including removals/fallbacks)
1423
- if update_model_values:
1424
- await _update_langflow_model_values(current_config, flows_service)
1425
-
1426
- logger.info("Completed asynchronous Langflow post-save sync")
1427
- except Exception as e:
1428
- # Do not fail user request if async sync fails; keep parity with existing behavior.
1429
- logger.error(f"Failed to update Langflow settings asynchronously: {str(e)}")
1430
-
1431
-
1432
1383
  async def _update_mcp_servers_with_provider_credentials(config, session_manager = None, flows_service=None):
1433
1384
  # Update MCP servers with provider credentials
1434
1385
  try:
@@ -1641,10 +1592,8 @@ async def reapply_all_settings(session_manager = None):
1641
1592
 
1642
1593
  async def rollback_onboarding(
1643
1594
  request: Request,
1644
- body: Optional[RollbackBody] = None,
1645
1595
  session_manager=Depends(get_session_manager),
1646
1596
  task_service=Depends(get_task_service),
1647
- knowledge_filter_service=Depends(get_knowledge_filter_service),
1648
1597
  user: User = Depends(get_current_user),
1649
1598
  ) -> RollbackResponse:
1650
1599
  """Rollback onboarding configuration when sample data files fail.
@@ -1674,36 +1623,7 @@ async def rollback_onboarding(
1674
1623
  cancelled_tasks = []
1675
1624
  deleted_files = []
1676
1625
 
1677
- # Delete knowledge filters created during onboarding
1678
- try:
1679
- async def remove_filter(filter_id: Optional[str]):
1680
- if filter_id and knowledge_filter_service:
1681
- try:
1682
- result = await knowledge_filter_service.delete_knowledge_filter(
1683
- filter_id, user.user_id, user.jwt_token
1684
- )
1685
- if result and result.get("success"):
1686
- logger.info(f"Deleted knowledge filter {filter_id}")
1687
- else:
1688
- error_msg = result.get("error") if result else "Unknown error"
1689
- logger.warning(f"Could not delete knowledge filter {filter_id}: {error_msg}")
1690
- except Exception as e:
1691
- logger.warning(f"Exception deleting knowledge filter {filter_id}: {str(e)}")
1692
-
1693
- if getattr(current_config.onboarding, 'openrag_docs_filter_id', None):
1694
- await remove_filter(current_config.onboarding.openrag_docs_filter_id)
1695
- current_config.onboarding.openrag_docs_filter_id = None
1696
-
1697
- if getattr(current_config.onboarding, 'user_doc_filter_id', None):
1698
- await remove_filter(current_config.onboarding.user_doc_filter_id)
1699
- current_config.onboarding.user_doc_filter_id = None
1700
- except Exception as e:
1701
- logger.error(f"Error while cleaning up knowledge filters: {e}")
1702
-
1703
1626
  # Cancel all active tasks and collect successfully ingested files
1704
- from session_manager import AnonymousUser
1705
- anonymous_user_id = AnonymousUser().user_id
1706
-
1707
1627
  for task_data in all_tasks:
1708
1628
  task_id = task_data.get("task_id")
1709
1629
  task_status = task_data.get("status")
@@ -1718,40 +1638,41 @@ async def rollback_onboarding(
1718
1638
  except Exception as e:
1719
1639
  logger.error(f"Failed to cancel task {task_id}: {str(e)}")
1720
1640
 
1721
- # Delete all files associated with any task, regardless of whether
1722
- # the task failed or completed, to ensure no partial chunks remain in OpenSearch.
1723
- files = task_data.get("files", {})
1724
- if isinstance(files, dict):
1725
- for file_path, file_info in files.items():
1726
- if isinstance(file_info, dict):
1727
- filename = file_info.get("filename") or file_path.split("/")[-1]
1728
- if filename:
1729
- try:
1730
- opensearch_client = session_manager.get_user_opensearch_client(
1731
- user.user_id, jwt_token
1732
- )
1733
- from utils.opensearch_queries import build_filename_delete_body
1734
- from config.settings import get_index_name
1735
-
1736
- delete_query = build_filename_delete_body(filename)
1737
- result = await opensearch_client.delete_by_query(
1738
- index=get_index_name(),
1739
- body=delete_query,
1740
- conflicts="proceed"
1741
- )
1742
- deleted_count = result.get("deleted", 0)
1743
- if deleted_count > 0:
1744
- deleted_files.append(filename)
1745
- logger.info(f"Deleted {deleted_count} chunks for filename {filename}")
1746
- except Exception as e:
1747
- logger.error(f"Failed to delete documents for {filename}: {str(e)}")
1748
-
1749
- # Wipe the task completely from memory so the frontend doesn't see it anymore
1750
- for check_user_id in [user.user_id, anonymous_user_id]:
1751
- if check_user_id in task_service.task_store and task_id in task_service.task_store[check_user_id]:
1752
- task_service._task_locks.pop(task_id, None)
1753
- task_service.task_store[check_user_id].pop(task_id, None)
1754
- logger.info(f"Purged task {task_id} completely from task_store for user {check_user_id}")
1641
+ # For completed tasks, find successfully ingested files and delete them
1642
+ elif task_status == "completed":
1643
+ files = task_data.get("files", {})
1644
+ if isinstance(files, dict):
1645
+ for file_path, file_info in files.items():
1646
+ # Check if file was successfully ingested
1647
+ if isinstance(file_info, dict):
1648
+ file_status = file_info.get("status")
1649
+ filename = file_info.get("filename") or file_path.split("/")[-1]
1650
+
1651
+ if file_status == "completed" and filename:
1652
+ try:
1653
+ # Get user's OpenSearch client
1654
+ opensearch_client = session_manager.get_user_opensearch_client(
1655
+ user.user_id, jwt_token
1656
+ )
1657
+
1658
+ # Delete documents by filename
1659
+ from utils.opensearch_queries import build_filename_delete_body
1660
+ from config.settings import get_index_name
1661
+
1662
+ delete_query = build_filename_delete_body(filename)
1663
+
1664
+ result = await opensearch_client.delete_by_query(
1665
+ index=get_index_name(),
1666
+ body=delete_query,
1667
+ conflicts="proceed"
1668
+ )
1669
+
1670
+ deleted_count = result.get("deleted", 0)
1671
+ if deleted_count > 0:
1672
+ deleted_files.append(filename)
1673
+ logger.info(f"Deleted {deleted_count} chunks for filename {filename}")
1674
+ except Exception as e:
1675
+ logger.error(f"Failed to delete documents for {filename}: {str(e)}")
1755
1676
 
1756
1677
  # Clear embedding provider and model settings
1757
1678
  current_config.knowledge.embedding_provider = "openai" # Reset to default
@@ -1759,21 +1680,11 @@ async def rollback_onboarding(
1759
1680
  current_config.onboarding.openrag_docs_ingested_version = None
1760
1681
  current_config.onboarding.openrag_docs_remote_signature = None
1761
1682
 
1762
- embedding_only = body.embedding_only if body else False
1763
-
1764
1683
  # Mark config as not edited so user can go through onboarding again
1765
- if not embedding_only:
1766
- current_config.edited = False
1767
- current_config.onboarding.current_step = 0
1768
- # Also clear LLM provider and model settings when doing a full rollback
1769
- current_config.agent.llm_provider = "openai" # Reset to default
1770
- current_config.agent.llm_model = ""
1771
- else:
1772
- # When rolling back embedding only, we keep edited=True
1773
- # and set current_step to 1 (which is the embedding step)
1774
- current_config.onboarding.current_step = 1
1684
+ current_config.edited = False
1685
+ current_config.onboarding.current_step = 0
1775
1686
 
1776
- # Save the rolled back configuration manually
1687
+ # Save the rolled back configuration manually to avoid save_config_file setting edited=True
1777
1688
  try:
1778
1689
  import yaml
1779
1690
  config_file = config_manager.config_file
@@ -1781,14 +1692,14 @@ async def rollback_onboarding(
1781
1692
  # Ensure directory exists
1782
1693
  config_file.parent.mkdir(parents=True, exist_ok=True)
1783
1694
 
1784
- # Save config with current edited state
1695
+ # Save config with edited=False
1785
1696
  with open(config_file, "w") as f:
1786
1697
  yaml.dump(current_config.to_dict(), f, default_flow_style=False, indent=2)
1787
1698
 
1788
1699
  # Update cached config
1789
1700
  config_manager._config = current_config
1790
1701
 
1791
- logger.info(f"Successfully saved rolled back configuration with edited={current_config.edited}")
1702
+ logger.info("Successfully saved rolled back configuration with edited=False")
1792
1703
  except Exception as e:
1793
1704
  logger.error(f"Failed to save rolled back configuration: {e}")
1794
1705
  return JSONResponse(
@@ -3,6 +3,7 @@ import os
3
3
  from utils.env_utils import get_env_int, get_env_float
4
4
 
5
5
  import httpx
6
+ from agentd.patch import patch_openai_with_mcp
6
7
  from dotenv import load_dotenv
7
8
  from openai import AsyncOpenAI
8
9
  from opensearchpy import AsyncOpenSearch
@@ -536,14 +537,16 @@ class AppClients:
536
537
  use_http2 = future.result(timeout=15)
537
538
 
538
539
  if use_http2:
539
- self._patched_async_client = AsyncOpenAI()
540
+ self._patched_async_client = patch_openai_with_mcp(AsyncOpenAI())
540
541
  logger.info("OpenAI client initialized with HTTP/2")
541
542
  else:
542
543
  http_client = httpx.AsyncClient(
543
544
  http2=False,
544
545
  timeout=httpx.Timeout(60.0, connect=10.0)
545
546
  )
546
- self._patched_async_client = AsyncOpenAI(http_client=http_client)
547
+ self._patched_async_client = patch_openai_with_mcp(
548
+ AsyncOpenAI(http_client=http_client)
549
+ )
547
550
  logger.info("OpenAI client initialized with HTTP/1.1 (fallback)")
548
551
  logger.info("Successfully initialized OpenAI client")
549
552
  except Exception as e:
@@ -410,14 +410,13 @@ async def ingest_openrag_docs_when_ready(
410
410
  ):
411
411
  """Ingest OpenRAG docs during onboarding."""
412
412
  use_url_ingest = _should_use_url_default_docs_ingest()
413
- task_id = None
414
413
  if use_url_ingest:
415
414
  try:
416
415
  await TelemetryClient.send_event(
417
416
  Category.DOCUMENT_INGESTION, MessageId.ORB_DOC_DEFAULT_URL_START
418
417
  )
419
418
  if DISABLE_INGEST_WITH_LANGFLOW:
420
- task_id = await _ingest_default_documents_url(
419
+ await _ingest_default_documents_url(
421
420
  document_service=document_service,
422
421
  docs_url=DEFAULT_DOCS_URL,
423
422
  crawl_depth=DEFAULT_DOCS_CRAWL_DEPTH,
@@ -427,7 +426,7 @@ async def ingest_openrag_docs_when_ready(
427
426
  "Ingesting default documents using Langflow",
428
427
  docs_url=DEFAULT_DOCS_URL,
429
428
  )
430
- task_id = await _ingest_default_documents_url_langflow(
429
+ await _ingest_default_documents_url_langflow(
431
430
  langflow_file_service=langflow_file_service,
432
431
  session_manager=session_manager,
433
432
  task_service=task_service,
@@ -442,7 +441,6 @@ async def ingest_openrag_docs_when_ready(
442
441
  await TelemetryClient.send_event(
443
442
  Category.DOCUMENT_INGESTION, MessageId.ORB_DOC_DEFAULT_URL_FAILED
444
443
  )
445
- return task_id
446
444
 
447
445
 
448
446
  async def ingest_default_documents_when_ready(
@@ -458,11 +456,9 @@ async def ingest_default_documents_when_ready(
458
456
  await TelemetryClient.send_event(
459
457
  Category.DOCUMENT_INGESTION, MessageId.ORB_DOC_DEFAULT_START
460
458
  )
461
- task_id = None
462
- if _should_use_url_default_docs_ingest():
463
- task_id = await ingest_openrag_docs_when_ready(
464
- document_service, task_service, langflow_file_service, session_manager
465
- )
459
+ await ingest_openrag_docs_when_ready(
460
+ document_service, task_service, langflow_file_service, session_manager
461
+ )
466
462
 
467
463
  base_dir = _get_documents_dir()
468
464
  if not os.path.isdir(base_dir):
@@ -473,7 +469,8 @@ async def ingest_default_documents_when_ready(
473
469
  excluded_files = set(EXCLUDED_INGESTION_FILES)
474
470
  if _should_use_url_default_docs_ingest():
475
471
  excluded_files.update(URL_INGEST_EXCLUDED_INGESTION_FILES)
476
-
472
+
473
+ # Collect files recursively, excluding warmup files and URL-ingested docs
477
474
  file_paths = [
478
475
  os.path.join(root, fn)
479
476
  for root, _, files in os.walk(base_dir)
@@ -485,22 +482,18 @@ async def ingest_default_documents_when_ready(
485
482
  raise FileNotFoundError(f"No default documents found in {base_dir}")
486
483
 
487
484
  if DISABLE_INGEST_WITH_LANGFLOW:
488
- new_task_id = await _ingest_default_documents_openrag(
489
- document_service, task_service, file_paths, existing_task_id=task_id, connector_type="local"
485
+ await _ingest_default_documents_openrag(
486
+ document_service, task_service, file_paths
490
487
  )
491
- task_id = new_task_id or task_id
492
488
  else:
493
- new_task_id = await _ingest_default_documents_langflow(
494
- langflow_file_service, session_manager, task_service, file_paths, existing_task_id=task_id, connector_type="local"
489
+ await _ingest_default_documents_langflow(
490
+ langflow_file_service, session_manager, task_service, file_paths
495
491
  )
496
- task_id = new_task_id or task_id
497
492
 
498
493
  await TelemetryClient.send_event(
499
494
  Category.DOCUMENT_INGESTION, MessageId.ORB_DOC_DEFAULT_COMPLETE
500
495
  )
501
496
 
502
- return task_id
503
-
504
497
  except Exception as e:
505
498
  logger.error("Default documents ingestion failed", error=str(e))
506
499
  await TelemetryClient.send_event(
@@ -510,7 +503,7 @@ async def ingest_default_documents_when_ready(
510
503
 
511
504
 
512
505
  async def _ingest_default_documents_langflow(
513
- langflow_file_service, session_manager, task_service, file_paths, existing_task_id: str = None, connector_type: str = "openrag_docs"
506
+ langflow_file_service, session_manager, task_service, file_paths
514
507
  ):
515
508
  """Ingest default documents using Langflow upload-ingest-delete pipeline."""
516
509
 
@@ -545,7 +538,7 @@ async def _ingest_default_documents_langflow(
545
538
  {"key": "owner", "value": None},
546
539
  {"key": "owner_name", "value": anonymous_user.name},
547
540
  {"key": "owner_email", "value": anonymous_user.email},
548
- {"key": "connector_type", "value": "openrag_docs"},
541
+ {"key": "connector_type", "value": "system_default"},
549
542
  {"key": "is_sample_data", "value": "true"},
550
543
  ]
551
544
  }
@@ -565,8 +558,6 @@ async def _ingest_default_documents_langflow(
565
558
  settings=None, # Use default ingestion settings
566
559
  delete_after_ingest=True, # Clean up after ingestion
567
560
  replace_duplicates=True,
568
- connector_type=connector_type,
569
- existing_task_id=existing_task_id,
570
561
  )
571
562
 
572
563
  logger.info(
@@ -574,7 +565,6 @@ async def _ingest_default_documents_langflow(
574
565
  task_id=task_id,
575
566
  file_count=len(file_paths),
576
567
  )
577
- return task_id
578
568
 
579
569
 
580
570
  async def _ingest_default_documents_url_langflow(
@@ -636,7 +626,6 @@ async def _ingest_default_documents_url_langflow(
636
626
  task_id=task_id,
637
627
  docs_url=docs_url,
638
628
  )
639
- return task_id
640
629
 
641
630
 
642
631
  async def _ingest_default_documents_url(
@@ -668,7 +657,7 @@ async def _ingest_default_documents_url(
668
657
  owner_name=None,
669
658
  owner_email=None,
670
659
  is_sample_data=True,
671
- connector_type="openrag_docs",
660
+ connector_type="system_default",
672
661
  )
673
662
  await processor.process_document_standard(
674
663
  file_path=temp_file_path,
@@ -679,7 +668,7 @@ async def _ingest_default_documents_url(
679
668
  owner_name=None,
680
669
  owner_email=None,
681
670
  file_size=os.path.getsize(temp_file_path),
682
- connector_type="openrag_docs",
671
+ connector_type="system_default",
683
672
  is_sample_data=True,
684
673
  )
685
674
  finally:
@@ -1046,7 +1035,7 @@ async def opensearch_health_ready(request):
1046
1035
 
1047
1036
 
1048
1037
  async def _ingest_default_documents_openrag(
1049
- document_service, task_service, file_paths, existing_task_id: str = None, connector_type: str = "openrag_docs"
1038
+ document_service, task_service, file_paths, connector_type: str = "local"
1050
1039
  ):
1051
1040
  """Ingest default documents using traditional OpenRAG processor."""
1052
1041
  logger.info(
@@ -1067,13 +1056,12 @@ async def _ingest_default_documents_openrag(
1067
1056
  connector_type=connector_type,
1068
1057
  )
1069
1058
 
1070
- task_id = await task_service.create_custom_task("anonymous", file_paths, processor, existing_task_id=existing_task_id)
1059
+ task_id = await task_service.create_custom_task("anonymous", file_paths, processor)
1071
1060
  logger.info(
1072
1061
  "Started traditional OpenRAG ingestion task",
1073
1062
  task_id=task_id,
1074
1063
  file_count=len(file_paths),
1075
1064
  )
1076
- return task_id
1077
1065
 
1078
1066
 
1079
1067
  async def _update_mcp_servers_with_provider_credentials(services):
@@ -1210,20 +1198,6 @@ async def startup_tasks(services):
1210
1198
  # Update MCP servers with provider credentials (especially important for no-auth mode)
1211
1199
  await _update_mcp_servers_with_provider_credentials(services)
1212
1200
 
1213
- # Ensure all configured flows exist in Langflow (create-only, never overwrites).
1214
- # This replaces LANGFLOW_LOAD_FLOWS_PATH, which performed a blind upsert on
1215
- # every container start and discarded any user edits made in the Langflow UI.
1216
- newly_created: set[str] = set()
1217
- try:
1218
- flows_service = services["flows_service"]
1219
- newly_created = await flows_service.ensure_flows_exist()
1220
- except Exception as e:
1221
- logger.error(
1222
- "Failed to ensure Langflow flows exist at startup — "
1223
- "flows may be missing until the next restart",
1224
- error=str(e),
1225
- )
1226
-
1227
1201
  # Check if flows were reset and reapply settings if config is edited
1228
1202
  try:
1229
1203
  config = get_openrag_config()
@@ -1231,9 +1205,6 @@ async def startup_tasks(services):
1231
1205
  logger.info("Checking if Langflow flows were reset")
1232
1206
  flows_service = services["flows_service"]
1233
1207
  reset_flows = await flows_service.check_flows_reset()
1234
- # Exclude flows that were just seeded — they match the JSON by design,
1235
- # not because they were externally reset.
1236
- reset_flows = [f for f in reset_flows if f not in newly_created]
1237
1208
 
1238
1209
  if reset_flows:
1239
1210
  logger.info(