OpenRAG 0.3.0__tar.gz → 0.3.2__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 (153) hide show
  1. {openrag-0.3.0/src/openrag.egg-info → openrag-0.3.2}/PKG-INFO +6 -6
  2. {openrag-0.3.0 → openrag-0.3.2}/README.md +5 -5
  3. {openrag-0.3.0 → openrag-0.3.2}/pyproject.toml +1 -1
  4. {openrag-0.3.0 → openrag-0.3.2}/src/api/documents.py +94 -39
  5. {openrag-0.3.0 → openrag-0.3.2}/src/api/search.py +3 -0
  6. {openrag-0.3.0 → openrag-0.3.2}/src/api/settings.py +114 -39
  7. {openrag-0.3.0 → openrag-0.3.2}/src/api/v1/documents.py +8 -28
  8. {openrag-0.3.0 → openrag-0.3.2}/src/api/v1/search.py +4 -0
  9. {openrag-0.3.0 → openrag-0.3.2}/src/config/config_manager.py +4 -1
  10. {openrag-0.3.0 → openrag-0.3.2}/src/config/settings.py +14 -0
  11. openrag-0.3.2/src/main.py +1933 -0
  12. {openrag-0.3.0 → openrag-0.3.2}/src/models/processors.py +21 -4
  13. openrag-0.3.2/src/models/url.py +157 -0
  14. {openrag-0.3.0 → openrag-0.3.2/src/openrag.egg-info}/PKG-INFO +6 -6
  15. {openrag-0.3.0 → openrag-0.3.2}/src/openrag.egg-info/SOURCES.txt +1 -0
  16. {openrag-0.3.0 → openrag-0.3.2}/src/services/auth_service.py +3 -2
  17. {openrag-0.3.0 → openrag-0.3.2}/src/services/chat_service.py +8 -3
  18. {openrag-0.3.0 → openrag-0.3.2}/src/services/flows_service.py +299 -471
  19. {openrag-0.3.0 → openrag-0.3.2}/src/services/langflow_file_service.py +236 -3
  20. {openrag-0.3.0 → openrag-0.3.2}/src/services/models_service.py +0 -1
  21. {openrag-0.3.0 → openrag-0.3.2}/src/services/search_service.py +25 -4
  22. {openrag-0.3.0 → openrag-0.3.2}/src/services/task_service.py +32 -0
  23. {openrag-0.3.0 → openrag-0.3.2}/src/tui/_assets/docker-compose.yml +5 -5
  24. {openrag-0.3.0 → openrag-0.3.2}/src/tui/_assets/flows/ingestion_flow.json +2 -2
  25. {openrag-0.3.0 → openrag-0.3.2}/src/tui/_assets/flows/openrag_agent.json +2 -2
  26. {openrag-0.3.0 → openrag-0.3.2}/src/tui/_assets/flows/openrag_nudges.json +5 -5
  27. {openrag-0.3.0 → openrag-0.3.2}/src/tui/_assets/flows/openrag_url_mcp.json +2 -2
  28. {openrag-0.3.0 → openrag-0.3.2}/src/utils/container_utils.py +18 -28
  29. {openrag-0.3.0 → openrag-0.3.2}/src/utils/embedding_fields.py +15 -2
  30. {openrag-0.3.0 → openrag-0.3.2}/src/utils/langflow_headers.py +14 -6
  31. {openrag-0.3.0 → openrag-0.3.2}/src/utils/opensearch_queries.py +1 -1
  32. {openrag-0.3.0 → openrag-0.3.2}/src/utils/opensearch_utils.py +41 -0
  33. {openrag-0.3.0 → openrag-0.3.2}/src/utils/telemetry/message_id.py +14 -0
  34. openrag-0.3.0/src/main.py +0 -988
  35. {openrag-0.3.0 → openrag-0.3.2}/LICENSE +0 -0
  36. {openrag-0.3.0 → openrag-0.3.2}/MANIFEST.in +0 -0
  37. {openrag-0.3.0 → openrag-0.3.2}/setup.cfg +0 -0
  38. {openrag-0.3.0 → openrag-0.3.2}/src/agent.py +0 -0
  39. {openrag-0.3.0 → openrag-0.3.2}/src/api/__init__.py +0 -0
  40. {openrag-0.3.0 → openrag-0.3.2}/src/api/auth.py +0 -0
  41. {openrag-0.3.0 → openrag-0.3.2}/src/api/chat.py +0 -0
  42. {openrag-0.3.0 → openrag-0.3.2}/src/api/connector_router.py +0 -0
  43. {openrag-0.3.0 → openrag-0.3.2}/src/api/connectors.py +0 -0
  44. {openrag-0.3.0 → openrag-0.3.2}/src/api/docling.py +0 -0
  45. {openrag-0.3.0 → openrag-0.3.2}/src/api/flows.py +0 -0
  46. {openrag-0.3.0 → openrag-0.3.2}/src/api/keys.py +0 -0
  47. {openrag-0.3.0 → openrag-0.3.2}/src/api/knowledge_filter.py +0 -0
  48. {openrag-0.3.0 → openrag-0.3.2}/src/api/langflow_files.py +0 -0
  49. {openrag-0.3.0 → openrag-0.3.2}/src/api/models.py +0 -0
  50. {openrag-0.3.0 → openrag-0.3.2}/src/api/nudges.py +0 -0
  51. {openrag-0.3.0 → openrag-0.3.2}/src/api/oidc.py +0 -0
  52. {openrag-0.3.0 → openrag-0.3.2}/src/api/provider_health.py +0 -0
  53. {openrag-0.3.0 → openrag-0.3.2}/src/api/provider_validation.py +0 -0
  54. {openrag-0.3.0 → openrag-0.3.2}/src/api/router.py +0 -0
  55. {openrag-0.3.0 → openrag-0.3.2}/src/api/tasks.py +0 -0
  56. {openrag-0.3.0 → openrag-0.3.2}/src/api/upload.py +0 -0
  57. {openrag-0.3.0 → openrag-0.3.2}/src/api/v1/__init__.py +0 -0
  58. {openrag-0.3.0 → openrag-0.3.2}/src/api/v1/chat.py +0 -0
  59. {openrag-0.3.0 → openrag-0.3.2}/src/api/v1/knowledge_filters.py +0 -0
  60. {openrag-0.3.0 → openrag-0.3.2}/src/api/v1/models.py +0 -0
  61. {openrag-0.3.0 → openrag-0.3.2}/src/api/v1/settings.py +0 -0
  62. {openrag-0.3.0 → openrag-0.3.2}/src/auth_context.py +0 -0
  63. {openrag-0.3.0 → openrag-0.3.2}/src/config/__init__.py +0 -0
  64. {openrag-0.3.0 → openrag-0.3.2}/src/config/model_constants.py +0 -0
  65. {openrag-0.3.0 → openrag-0.3.2}/src/connectors/__init__.py +0 -0
  66. {openrag-0.3.0 → openrag-0.3.2}/src/connectors/base.py +0 -0
  67. {openrag-0.3.0 → openrag-0.3.2}/src/connectors/connection_manager.py +0 -0
  68. {openrag-0.3.0 → openrag-0.3.2}/src/connectors/google_drive/__init__.py +0 -0
  69. {openrag-0.3.0 → openrag-0.3.2}/src/connectors/google_drive/connector.py +0 -0
  70. {openrag-0.3.0 → openrag-0.3.2}/src/connectors/google_drive/oauth.py +0 -0
  71. {openrag-0.3.0 → openrag-0.3.2}/src/connectors/langflow_connector_service.py +0 -0
  72. {openrag-0.3.0 → openrag-0.3.2}/src/connectors/onedrive/__init__.py +0 -0
  73. {openrag-0.3.0 → openrag-0.3.2}/src/connectors/onedrive/connector.py +0 -0
  74. {openrag-0.3.0 → openrag-0.3.2}/src/connectors/onedrive/oauth.py +0 -0
  75. {openrag-0.3.0 → openrag-0.3.2}/src/connectors/service.py +0 -0
  76. {openrag-0.3.0 → openrag-0.3.2}/src/connectors/sharepoint/__init__.py +0 -0
  77. {openrag-0.3.0 → openrag-0.3.2}/src/connectors/sharepoint/connector.py +0 -0
  78. {openrag-0.3.0 → openrag-0.3.2}/src/connectors/sharepoint/oauth.py +0 -0
  79. {openrag-0.3.0 → openrag-0.3.2}/src/connectors/sharepoint/utils.py +0 -0
  80. {openrag-0.3.0 → openrag-0.3.2}/src/dependencies.py +0 -0
  81. {openrag-0.3.0 → openrag-0.3.2}/src/models/__init__.py +0 -0
  82. {openrag-0.3.0 → openrag-0.3.2}/src/models/tasks.py +0 -0
  83. {openrag-0.3.0 → openrag-0.3.2}/src/openrag.egg-info/dependency_links.txt +0 -0
  84. {openrag-0.3.0 → openrag-0.3.2}/src/openrag.egg-info/entry_points.txt +0 -0
  85. {openrag-0.3.0 → openrag-0.3.2}/src/openrag.egg-info/requires.txt +0 -0
  86. {openrag-0.3.0 → openrag-0.3.2}/src/openrag.egg-info/top_level.txt +0 -0
  87. {openrag-0.3.0 → openrag-0.3.2}/src/services/__init__.py +0 -0
  88. {openrag-0.3.0 → openrag-0.3.2}/src/services/api_key_service.py +0 -0
  89. {openrag-0.3.0 → openrag-0.3.2}/src/services/conversation_persistence_service.py +0 -0
  90. {openrag-0.3.0 → openrag-0.3.2}/src/services/document_service.py +0 -0
  91. {openrag-0.3.0 → openrag-0.3.2}/src/services/knowledge_filter_service.py +0 -0
  92. {openrag-0.3.0 → openrag-0.3.2}/src/services/langflow_history_service.py +0 -0
  93. {openrag-0.3.0 → openrag-0.3.2}/src/services/langflow_mcp_service.py +0 -0
  94. {openrag-0.3.0 → openrag-0.3.2}/src/services/monitor_service.py +0 -0
  95. {openrag-0.3.0 → openrag-0.3.2}/src/services/session_ownership_service.py +0 -0
  96. {openrag-0.3.0 → openrag-0.3.2}/src/session_manager.py +0 -0
  97. {openrag-0.3.0 → openrag-0.3.2}/src/tui/__init__.py +0 -0
  98. {openrag-0.3.0 → openrag-0.3.2}/src/tui/_assets/docker-compose.gpu.yml +0 -0
  99. {openrag-0.3.0 → openrag-0.3.2}/src/tui/_assets/flows/components/ollama_embedding.json +0 -0
  100. {openrag-0.3.0 → openrag-0.3.2}/src/tui/_assets/flows/components/ollama_llm.json +0 -0
  101. {openrag-0.3.0 → openrag-0.3.2}/src/tui/_assets/flows/components/ollama_llm_text.json +0 -0
  102. {openrag-0.3.0 → openrag-0.3.2}/src/tui/_assets/flows/components/watsonx_embedding.json +0 -0
  103. {openrag-0.3.0 → openrag-0.3.2}/src/tui/_assets/flows/components/watsonx_llm.json +0 -0
  104. {openrag-0.3.0 → openrag-0.3.2}/src/tui/_assets/flows/components/watsonx_llm_text.json +0 -0
  105. {openrag-0.3.0 → openrag-0.3.2}/src/tui/_assets/openrag-documents/docling.pdf +0 -0
  106. {openrag-0.3.0 → openrag-0.3.2}/src/tui/_assets/openrag-documents/ibm_anthropic.pdf +0 -0
  107. {openrag-0.3.0 → openrag-0.3.2}/src/tui/_assets/openrag-documents/openrag-documentation.pdf +0 -0
  108. {openrag-0.3.0 → openrag-0.3.2}/src/tui/_assets/openrag-documents/warmup_ocr.pdf +0 -0
  109. {openrag-0.3.0 → openrag-0.3.2}/src/tui/cli.py +0 -0
  110. {openrag-0.3.0 → openrag-0.3.2}/src/tui/config_fields.py +0 -0
  111. {openrag-0.3.0 → openrag-0.3.2}/src/tui/main.py +0 -0
  112. {openrag-0.3.0 → openrag-0.3.2}/src/tui/managers/__init__.py +0 -0
  113. {openrag-0.3.0 → openrag-0.3.2}/src/tui/managers/container_manager.py +0 -0
  114. {openrag-0.3.0 → openrag-0.3.2}/src/tui/managers/docling_manager.py +0 -0
  115. {openrag-0.3.0 → openrag-0.3.2}/src/tui/managers/env_manager.py +0 -0
  116. {openrag-0.3.0 → openrag-0.3.2}/src/tui/screens/__init__.py +0 -0
  117. {openrag-0.3.0 → openrag-0.3.2}/src/tui/screens/config.py +0 -0
  118. {openrag-0.3.0 → openrag-0.3.2}/src/tui/screens/diagnostics.py +0 -0
  119. {openrag-0.3.0 → openrag-0.3.2}/src/tui/screens/logs.py +0 -0
  120. {openrag-0.3.0 → openrag-0.3.2}/src/tui/screens/monitor.py +0 -0
  121. {openrag-0.3.0 → openrag-0.3.2}/src/tui/screens/welcome.py +0 -0
  122. {openrag-0.3.0 → openrag-0.3.2}/src/tui/utils/__init__.py +0 -0
  123. {openrag-0.3.0 → openrag-0.3.2}/src/tui/utils/clipboard.py +0 -0
  124. {openrag-0.3.0 → openrag-0.3.2}/src/tui/utils/platform.py +0 -0
  125. {openrag-0.3.0 → openrag-0.3.2}/src/tui/utils/startup_checks.py +0 -0
  126. {openrag-0.3.0 → openrag-0.3.2}/src/tui/utils/validation.py +0 -0
  127. {openrag-0.3.0 → openrag-0.3.2}/src/tui/utils/version_check.py +0 -0
  128. {openrag-0.3.0 → openrag-0.3.2}/src/tui/widgets/__init__.py +0 -0
  129. {openrag-0.3.0 → openrag-0.3.2}/src/tui/widgets/command_modal.py +0 -0
  130. {openrag-0.3.0 → openrag-0.3.2}/src/tui/widgets/diagnostics_notification.py +0 -0
  131. {openrag-0.3.0 → openrag-0.3.2}/src/tui/widgets/error_notification.py +0 -0
  132. {openrag-0.3.0 → openrag-0.3.2}/src/tui/widgets/factory_reset_warning_modal.py +0 -0
  133. {openrag-0.3.0 → openrag-0.3.2}/src/tui/widgets/flow_backup_warning_modal.py +0 -0
  134. {openrag-0.3.0 → openrag-0.3.2}/src/tui/widgets/prune_options_modal.py +0 -0
  135. {openrag-0.3.0 → openrag-0.3.2}/src/tui/widgets/upgrade_instructions_modal.py +0 -0
  136. {openrag-0.3.0 → openrag-0.3.2}/src/tui/widgets/version_mismatch_warning_modal.py +0 -0
  137. {openrag-0.3.0 → openrag-0.3.2}/src/tui/widgets/waves.py +0 -0
  138. {openrag-0.3.0 → openrag-0.3.2}/src/utils/__init__.py +0 -0
  139. {openrag-0.3.0 → openrag-0.3.2}/src/utils/acl_utils.py +0 -0
  140. {openrag-0.3.0 → openrag-0.3.2}/src/utils/docling_client.py +0 -0
  141. {openrag-0.3.0 → openrag-0.3.2}/src/utils/document_processing.py +0 -0
  142. {openrag-0.3.0 → openrag-0.3.2}/src/utils/embeddings.py +0 -0
  143. {openrag-0.3.0 → openrag-0.3.2}/src/utils/env_utils.py +0 -0
  144. {openrag-0.3.0 → openrag-0.3.2}/src/utils/file_utils.py +0 -0
  145. {openrag-0.3.0 → openrag-0.3.2}/src/utils/gpu_detection.py +0 -0
  146. {openrag-0.3.0 → openrag-0.3.2}/src/utils/hash_utils.py +0 -0
  147. {openrag-0.3.0 → openrag-0.3.2}/src/utils/langflow_utils.py +0 -0
  148. {openrag-0.3.0 → openrag-0.3.2}/src/utils/logging_config.py +0 -0
  149. {openrag-0.3.0 → openrag-0.3.2}/src/utils/paths.py +0 -0
  150. {openrag-0.3.0 → openrag-0.3.2}/src/utils/telemetry/__init__.py +0 -0
  151. {openrag-0.3.0 → openrag-0.3.2}/src/utils/telemetry/category.py +0 -0
  152. {openrag-0.3.0 → openrag-0.3.2}/src/utils/telemetry/client.py +0 -0
  153. {openrag-0.3.0 → openrag-0.3.2}/src/utils/version_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openrag
3
- Version: 0.3.0
3
+ Version: 0.3.2
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
@@ -40,7 +40,7 @@ Dynamic: license-file
40
40
 
41
41
  <div align="center">
42
42
 
43
- <img src="./docs/static/img/openrag-logo-dog.svg" alt="OpenRAG Logo" width="120"/>
43
+ <img src="./docs/static/img/openrag-logo-dog.svg" alt="" width="120"/>
44
44
 
45
45
  # OpenRAG
46
46
 
@@ -49,7 +49,7 @@ Dynamic: license-file
49
49
  </h3>
50
50
 
51
51
  <!-- Badges -->
52
-
52
+
53
53
  [![Langflow](https://img.shields.io/badge/Langflow-1C1C1E?style=for-the-badge&logo=langflow)](https://github.com/langflow-ai/langflow)
54
54
  [![OpenSearch](https://img.shields.io/badge/OpenSearch-005EB8?style=for-the-badge&logo=opensearch&logoColor=white)](https://github.com/opensearch-project/OpenSearch)
55
55
  [![Docling](https://img.shields.io/badge/Docling-000000?style=for-the-badge)](https://github.com/docling-project/docling)
@@ -66,11 +66,11 @@ Dynamic: license-file
66
66
 
67
67
  OpenRAG is a comprehensive Retrieval-Augmented Generation platform that enables intelligent document search and AI-powered conversations.
68
68
 
69
- Users can upload, process, and query documents through a chat interface backed by large language models and semantic search capabilities. The system utilizes Langflow for document ingestion, retrieval workflows, and intelligent nudges, providing a seamless RAG experience.
69
+ Users can upload, process, and query documents through a chat interface backed by large language models and semantic search capabilities. The system utilizes Langflow for document ingestion, retrieval workflows, and intelligent nudges, providing a seamless RAG experience.
70
70
 
71
71
  Check out the [documentation](https://docs.openr.ag/) or get started with the [quickstart](https://docs.openr.ag/quickstart).
72
72
 
73
- Built with [Starlette](https://github.com/Kludex/starlette) and [Next.js](https://github.com/vercel/next.js).
73
+ Built with [FastAPI](https://fastapi.tiangolo.com/) and [Next.js](https://github.com/vercel/next.js).
74
74
  Powered by [OpenSearch](https://github.com/opensearch-project/OpenSearch), [Langflow](https://github.com/langflow-ai/langflow), and [Docling](https://github.com/docling-project/docling).
75
75
 
76
76
  ---
@@ -201,7 +201,7 @@ The MCP server provides tools for RAG-enhanced chat, semantic search, and settin
201
201
 
202
202
  For developers who want to [contribute to OpenRAG](https://docs.openr.ag/support/contribute) or set up a development environment, see [CONTRIBUTING.md](CONTRIBUTING.md).
203
203
 
204
- ## 🆘 Troubleshooting
204
+ ## 🛟 Troubleshooting
205
205
 
206
206
  For assistance with OpenRAG, see [Troubleshoot OpenRAG](https://docs.openr.ag/support/troubleshoot) and visit the [Discussions page](https://github.com/langflow-ai/openrag/discussions).
207
207
 
@@ -1,6 +1,6 @@
1
1
  <div align="center">
2
2
 
3
- <img src="./docs/static/img/openrag-logo-dog.svg" alt="OpenRAG Logo" width="120"/>
3
+ <img src="./docs/static/img/openrag-logo-dog.svg" alt="" width="120"/>
4
4
 
5
5
  # OpenRAG
6
6
 
@@ -9,7 +9,7 @@
9
9
  </h3>
10
10
 
11
11
  <!-- Badges -->
12
-
12
+
13
13
  [![Langflow](https://img.shields.io/badge/Langflow-1C1C1E?style=for-the-badge&logo=langflow)](https://github.com/langflow-ai/langflow)
14
14
  [![OpenSearch](https://img.shields.io/badge/OpenSearch-005EB8?style=for-the-badge&logo=opensearch&logoColor=white)](https://github.com/opensearch-project/OpenSearch)
15
15
  [![Docling](https://img.shields.io/badge/Docling-000000?style=for-the-badge)](https://github.com/docling-project/docling)
@@ -26,11 +26,11 @@
26
26
 
27
27
  OpenRAG is a comprehensive Retrieval-Augmented Generation platform that enables intelligent document search and AI-powered conversations.
28
28
 
29
- Users can upload, process, and query documents through a chat interface backed by large language models and semantic search capabilities. The system utilizes Langflow for document ingestion, retrieval workflows, and intelligent nudges, providing a seamless RAG experience.
29
+ Users can upload, process, and query documents through a chat interface backed by large language models and semantic search capabilities. The system utilizes Langflow for document ingestion, retrieval workflows, and intelligent nudges, providing a seamless RAG experience.
30
30
 
31
31
  Check out the [documentation](https://docs.openr.ag/) or get started with the [quickstart](https://docs.openr.ag/quickstart).
32
32
 
33
- Built with [Starlette](https://github.com/Kludex/starlette) and [Next.js](https://github.com/vercel/next.js).
33
+ Built with [FastAPI](https://fastapi.tiangolo.com/) and [Next.js](https://github.com/vercel/next.js).
34
34
  Powered by [OpenSearch](https://github.com/opensearch-project/OpenSearch), [Langflow](https://github.com/langflow-ai/langflow), and [Docling](https://github.com/docling-project/docling).
35
35
 
36
36
  ---
@@ -161,7 +161,7 @@ The MCP server provides tools for RAG-enhanced chat, semantic search, and settin
161
161
 
162
162
  For developers who want to [contribute to OpenRAG](https://docs.openr.ag/support/contribute) or set up a development environment, see [CONTRIBUTING.md](CONTRIBUTING.md).
163
163
 
164
- ## 🆘 Troubleshooting
164
+ ## 🛟 Troubleshooting
165
165
 
166
166
  For assistance with OpenRAG, see [Troubleshoot OpenRAG](https://docs.openr.ag/support/troubleshoot) and visit the [Discussions page](https://github.com/langflow-ai/openrag/discussions).
167
167
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "openrag"
7
- version = "0.3.0"
7
+ version = "0.3.2"
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"
@@ -13,6 +13,92 @@ class DeleteDocumentBody(BaseModel):
13
13
  filename: str
14
14
 
15
15
 
16
+ async def delete_documents_by_filename_core(
17
+ filename: str,
18
+ session_manager,
19
+ user_id: str,
20
+ jwt_token: str | None,
21
+ ):
22
+ """Shared delete-by-filename logic for v1 and non-v1 endpoints."""
23
+ from config.settings import get_index_name
24
+ from utils.opensearch_queries import build_filename_delete_body
25
+
26
+ normalized_filename = (filename or "").strip()
27
+ if not normalized_filename:
28
+ return (
29
+ {
30
+ "success": False,
31
+ "deleted_chunks": 0,
32
+ "filename": normalized_filename,
33
+ "message": None,
34
+ "error": "Filename is required",
35
+ },
36
+ 400,
37
+ )
38
+
39
+ try:
40
+ opensearch_client = session_manager.get_user_opensearch_client(
41
+ user_id, jwt_token
42
+ )
43
+ delete_query = build_filename_delete_body(normalized_filename)
44
+ result = await opensearch_client.delete_by_query(
45
+ index=get_index_name(),
46
+ body=delete_query,
47
+ conflicts="proceed",
48
+ )
49
+
50
+ deleted_count = result.get("deleted", 0)
51
+ logger.info(
52
+ f"Deleted {deleted_count} chunks for filename {normalized_filename}",
53
+ user_id=user_id,
54
+ )
55
+
56
+ if deleted_count == 0:
57
+ return (
58
+ {
59
+ "success": False,
60
+ "deleted_chunks": 0,
61
+ "filename": normalized_filename,
62
+ "message": None,
63
+ "error": "No matching document chunks were deleted. The file may be missing or not deletable in the current user context.",
64
+ },
65
+ 404,
66
+ )
67
+
68
+ return (
69
+ {
70
+ "success": True,
71
+ "deleted_chunks": deleted_count,
72
+ "filename": normalized_filename,
73
+ "message": f"All documents with filename '{normalized_filename}' deleted successfully",
74
+ "error": None,
75
+ },
76
+ 200,
77
+ )
78
+ except Exception as e:
79
+ logger.error(
80
+ "Error deleting documents by filename",
81
+ filename=normalized_filename,
82
+ error=str(e),
83
+ )
84
+ error_str = str(e)
85
+ status_code = 403 if "AuthenticationException" in error_str else 500
86
+ return (
87
+ {
88
+ "success": False,
89
+ "deleted_chunks": 0,
90
+ "filename": normalized_filename,
91
+ "message": None,
92
+ "error": (
93
+ "Access denied: insufficient permissions"
94
+ if status_code == 403
95
+ else "An internal error has occurred while deleting documents"
96
+ ),
97
+ },
98
+ status_code,
99
+ )
100
+
101
+
16
102
  async def _ensure_index_exists():
17
103
  """Create the OpenSearch index if it doesn't exist yet."""
18
104
  from main import init_index
@@ -70,43 +156,12 @@ async def delete_documents_by_filename(
70
156
  body: DeleteDocumentBody,
71
157
  session_manager=Depends(get_session_manager),
72
158
  user: User = Depends(get_current_user),
73
- ):
159
+ ):
74
160
  """Delete all documents with a specific filename"""
75
- from config.settings import get_index_name
76
-
77
- jwt_token = user.jwt_token
78
-
79
- try:
80
- opensearch_client = session_manager.get_user_opensearch_client(
81
- user.user_id, jwt_token
82
- )
83
-
84
- from utils.opensearch_queries import build_filename_delete_body
85
-
86
- delete_query = build_filename_delete_body(body.filename)
87
-
88
- logger.debug(f"Deleting documents with filename: {body.filename}")
89
-
90
- result = await opensearch_client.delete_by_query(
91
- index=get_index_name(),
92
- body=delete_query,
93
- conflicts="proceed"
94
- )
95
-
96
- deleted_count = result.get("deleted", 0)
97
- logger.info(f"Deleted {deleted_count} chunks for filename {body.filename}", user_id=user.user_id)
98
-
99
- return JSONResponse({
100
- "success": True,
101
- "deleted_chunks": deleted_count,
102
- "filename": body.filename,
103
- "message": f"All documents with filename '{body.filename}' deleted successfully"
104
- }, status_code=200)
105
-
106
- except Exception as e:
107
- logger.error("Error deleting documents by filename", filename=body.filename, error=str(e))
108
- error_str = str(e)
109
- if "AuthenticationException" in error_str:
110
- return JSONResponse({"error": "Access denied: insufficient permissions"}, status_code=403)
111
- else:
112
- return JSONResponse({"error": str(e)}, status_code=500)
161
+ payload, status_code =await delete_documents_by_filename_core(
162
+ filename=body.filename,
163
+ session_manager=session_manager,
164
+ user_id=user.user_id,
165
+ jwt_token=user.jwt_token,
166
+ )
167
+ return JSONResponse(payload, status_code=status_code)
@@ -4,6 +4,7 @@ from fastapi import Depends
4
4
  from pydantic import BaseModel, Field
5
5
  from fastapi.responses import JSONResponse
6
6
  from utils.logging_config import get_logger
7
+ from utils.opensearch_utils import OpenSearchDiskSpaceError, DISK_SPACE_ERROR_MESSAGE
7
8
 
8
9
  from dependencies import get_search_service, get_session_manager, get_current_user
9
10
  from session_manager import User
@@ -50,6 +51,8 @@ async def search(
50
51
  score_threshold=body.scoreThreshold,
51
52
  )
52
53
  return JSONResponse(result, status_code=200)
54
+ except OpenSearchDiskSpaceError:
55
+ return JSONResponse({"error": DISK_SPACE_ERROR_MESSAGE}, status_code=507)
53
56
  except Exception as e:
54
57
  error_msg = str(e)
55
58
  if (
@@ -3,10 +3,11 @@ import platform
3
3
  from fastapi import Depends, Request, HTTPException
4
4
  from fastapi.responses import JSONResponse
5
5
  from pydantic import BaseModel, Field
6
- from utils.container_utils import transform_localhost_url
7
6
  from utils.logging_config import get_logger
8
7
  from utils.telemetry import TelemetryClient, Category, MessageId
8
+ from utils.version_utils import OPENRAG_VERSION
9
9
  from config.settings import (
10
+ DEFAULT_DOCS_URL,
10
11
  DISABLE_INGEST_WITH_LANGFLOW,
11
12
  INGEST_SAMPLE_DATA,
12
13
  LANGFLOW_URL,
@@ -88,6 +89,8 @@ class OnboardingStateBody(BaseModel):
88
89
  upload_steps: Optional[Dict[str, Any]] = None
89
90
  openrag_docs_filter_id: Optional[str] = None
90
91
  user_doc_filter_id: Optional[str] = None
92
+ openrag_docs_ingested_version: Optional[str] = None
93
+ openrag_docs_remote_signature: Optional[str] = None
91
94
 
92
95
 
93
96
  class DoclingPresetBody(BaseModel):
@@ -105,6 +108,8 @@ class OnboardingStateConfig(BaseModel):
105
108
  upload_steps: Optional[Dict[str, Any]]
106
109
  openrag_docs_filter_id: Optional[str]
107
110
  user_doc_filter_id: Optional[str]
111
+ openrag_docs_ingested_version: Optional[str]
112
+ openrag_docs_remote_signature: Optional[str]
108
113
 
109
114
  class OpenAIProviderConfig(BaseModel):
110
115
  has_api_key: bool
@@ -174,6 +179,11 @@ class OnboardingResponse(BaseModel):
174
179
  openrag_docs_filter_id: Optional[str] = None
175
180
  task_id: Optional[str] = None
176
181
 
182
+
183
+ class RefreshOpenRAGDocsResponse(BaseModel):
184
+ message: str
185
+ refreshed: bool
186
+
177
187
  class DoclingConfig(BaseModel):
178
188
  do_ocr: bool
179
189
  ocr_engine: str
@@ -316,6 +326,8 @@ async def get_settings(
316
326
  upload_steps=openrag_config.onboarding.upload_steps,
317
327
  openrag_docs_filter_id=openrag_config.onboarding.openrag_docs_filter_id,
318
328
  user_doc_filter_id=openrag_config.onboarding.user_doc_filter_id,
329
+ openrag_docs_ingested_version=openrag_config.onboarding.openrag_docs_ingested_version,
330
+ openrag_docs_remote_signature=openrag_config.onboarding.openrag_docs_remote_signature,
319
331
  ),
320
332
  providers=ProvidersConfig(
321
333
  openai=OpenAIProviderConfig(
@@ -811,12 +823,12 @@ async def update_settings(
811
823
  flows_service = _get_flows_service()
812
824
 
813
825
  # Update global variables
814
- await _update_langflow_global_variables(current_config)
826
+ await _update_langflow_global_variables(current_config, flows_service=flows_service)
815
827
 
816
828
  # Update LLM client credentials when embedding selection changes
817
829
  if body.embedding_provider is not None or body.embedding_model is not None:
818
830
  await _update_mcp_servers_with_provider_credentials(
819
- current_config, session_manager
831
+ current_config, session_manager, flows_service=flows_service
820
832
  )
821
833
 
822
834
  # Update model values if provider or model changed (including removals that trigger fallback)
@@ -1072,11 +1084,12 @@ async def onboarding(
1072
1084
  # or if existing config has values (for OpenAI/Anthropic that might already be set)
1073
1085
  if (provider_fields_provided or
1074
1086
  current_config.providers.openai.api_key != "" or
1075
- current_config.providers.anthropic.api_key != ""):
1076
- await _update_langflow_global_variables(current_config)
1087
+ current_config.providers.anthropic.api_key != "" or
1088
+ current_config.providers.any_configured()):
1089
+ await _update_langflow_global_variables(current_config, flows_service=flows_service)
1077
1090
 
1078
1091
  if body.embedding_provider or body.embedding_model:
1079
- await _update_mcp_servers_with_provider_credentials(current_config, session_manager)
1092
+ await _update_mcp_servers_with_provider_credentials(current_config, session_manager=session_manager, flows_service=flows_service)
1080
1093
 
1081
1094
  # Update model values if provider or model fields were provided
1082
1095
  if body.llm_provider or body.llm_model or body.embedding_provider or body.embedding_model:
@@ -1126,6 +1139,18 @@ async def onboarding(
1126
1139
  langflow_file_service,
1127
1140
  session_manager,
1128
1141
  )
1142
+ current_config.onboarding.openrag_docs_ingested_version = OPENRAG_VERSION
1143
+ from main import (
1144
+ _get_remote_docs_signature,
1145
+ _should_use_url_default_docs_ingest,
1146
+ )
1147
+
1148
+ if _should_use_url_default_docs_ingest():
1149
+ current_config.onboarding.openrag_docs_remote_signature = (
1150
+ await _get_remote_docs_signature(DEFAULT_DOCS_URL)
1151
+ )
1152
+ else:
1153
+ current_config.onboarding.openrag_docs_remote_signature = None
1129
1154
  logger.info("Sample data ingestion completed successfully")
1130
1155
 
1131
1156
  except Exception as e:
@@ -1252,10 +1277,12 @@ async def _create_openrag_docs_filter(
1252
1277
  query_data = json.dumps({
1253
1278
  "query": "",
1254
1279
  "filters": {
1255
- "data_sources": ["openrag-documentation.pdf"],
1280
+ # URL-based docs ingestion produces many source URLs.
1281
+ # Filter by connector type to target OpenRAG docs only.
1282
+ "data_sources": ["*"],
1256
1283
  "document_types": ["*"],
1257
1284
  "owners": ["*"],
1258
- "connector_types": ["*"],
1285
+ "connector_types": ["openrag_docs"],
1259
1286
  },
1260
1287
  "limit": 10,
1261
1288
  "scoreThreshold": 0,
@@ -1293,15 +1320,15 @@ def _get_flows_service():
1293
1320
  return FlowsService()
1294
1321
 
1295
1322
 
1296
- async def _update_langflow_global_variables(config):
1323
+ async def _update_langflow_global_variables(config, flows_service=None):
1297
1324
  """Update Langflow global variables for all configured providers"""
1298
1325
  try:
1299
1326
  # WatsonX global variables
1300
1327
  if config.providers.watsonx.api_key:
1301
1328
  await clients._create_langflow_global_variable(
1302
- "WATSONX_API_KEY", config.providers.watsonx.api_key, modify=True
1329
+ "WATSONX_APIKEY", config.providers.watsonx.api_key, modify=True
1303
1330
  )
1304
- logger.info("Set WATSONX_API_KEY global variable in Langflow")
1331
+ logger.info("Set WATSONX_APIKEY global variable in Langflow")
1305
1332
 
1306
1333
  if config.providers.watsonx.project_id:
1307
1334
  await clients._create_langflow_global_variable(
@@ -1331,19 +1358,14 @@ async def _update_langflow_global_variables(config):
1331
1358
 
1332
1359
  # Ollama global variables
1333
1360
  if config.providers.ollama.endpoint:
1361
+ if not flows_service:
1362
+ flows_service = _get_flows_service()
1334
1363
 
1335
- try:
1336
- endpoint = transform_localhost_url(config.providers.ollama.endpoint, is_langflow=True, is_podman=True)
1337
- await clients._create_langflow_global_variable(
1338
- "OLLAMA_BASE_URL", endpoint, modify=True
1339
- )
1340
- logger.info("Set OLLAMA_BASE_URL global variable in Langflow (Podman)")
1341
- except Exception:
1342
- endpoint = transform_localhost_url(config.providers.ollama.endpoint, is_langflow=True, is_podman=False)
1343
- await clients._create_langflow_global_variable(
1344
- "OLLAMA_BASE_URL", endpoint, modify=True
1345
- )
1346
- logger.info("Set OLLAMA_BASE_URL global variable in Langflow (Docker)")
1364
+ endpoint = await flows_service.resolve_ollama_url(config.providers.ollama.endpoint, force_refresh=True)
1365
+ await clients._create_langflow_global_variable(
1366
+ "OLLAMA_BASE_URL", endpoint, modify=True
1367
+ )
1368
+ logger.info("Set OLLAMA_BASE_URL global variable in Langflow")
1347
1369
 
1348
1370
  if config.knowledge.embedding_model:
1349
1371
  await clients._create_langflow_global_variable(
@@ -1358,7 +1380,7 @@ async def _update_langflow_global_variables(config):
1358
1380
  raise
1359
1381
 
1360
1382
 
1361
- async def _update_mcp_servers_with_provider_credentials(config, session_manager = None):
1383
+ async def _update_mcp_servers_with_provider_credentials(config, session_manager = None, flows_service=None):
1362
1384
  # Update MCP servers with provider credentials
1363
1385
  try:
1364
1386
  from services.langflow_mcp_service import LangflowMCPService
@@ -1367,7 +1389,7 @@ async def _update_mcp_servers_with_provider_credentials(config, session_manager
1367
1389
  mcp_service = LangflowMCPService()
1368
1390
 
1369
1391
  # Build global vars using utility function
1370
- mcp_global_vars = build_mcp_global_vars_from_config(config)
1392
+ mcp_global_vars = await build_mcp_global_vars_from_config(config, flows_service=flows_service)
1371
1393
 
1372
1394
  # In no-auth mode, add the anonymous JWT token and user details
1373
1395
  if is_no_auth_mode() and session_manager:
@@ -1396,29 +1418,45 @@ async def _update_mcp_servers_with_provider_credentials(config, session_manager
1396
1418
 
1397
1419
 
1398
1420
  async def _update_langflow_model_values(config, flows_service):
1399
- """Update model values across Langflow flows"""
1421
+ """Update model values across Langflow flows for all configured providers"""
1400
1422
  try:
1401
- # Update LLM model values
1423
+ # 1. Update ONLY the current LLM provider
1402
1424
  llm_provider = config.agent.llm_provider.lower()
1403
-
1404
1425
  await flows_service.change_langflow_model_value(
1405
1426
  llm_provider,
1406
1427
  llm_model=config.agent.llm_model,
1428
+ force_llm_update=True
1407
1429
  )
1408
1430
  logger.info(
1409
1431
  f"Successfully updated Langflow flows for LLM provider {llm_provider}"
1410
1432
  )
1411
1433
 
1412
- # Update embedding model values
1413
- embedding_provider = config.knowledge.embedding_provider.lower()
1434
+ # 2. Update ALL configured embedding providers
1435
+ embedding_providers = []
1436
+ if config.providers.openai.configured:
1437
+ embedding_providers.append("openai")
1438
+ if config.providers.watsonx.configured:
1439
+ embedding_providers.append("watsonx")
1440
+ if config.providers.ollama.configured:
1441
+ embedding_providers.append("ollama")
1442
+
1443
+ current_embedding_provider = config.knowledge.embedding_provider.lower()
1444
+ for provider in embedding_providers:
1445
+ # Use configured model for current provider, or None (first available) for others
1446
+ embedding_model = (
1447
+ config.knowledge.embedding_model
1448
+ if provider == current_embedding_provider
1449
+ else None
1450
+ )
1414
1451
 
1415
- await flows_service.change_langflow_model_value(
1416
- embedding_provider,
1417
- embedding_model=config.knowledge.embedding_model,
1418
- )
1419
- logger.info(
1420
- f"Successfully updated Langflow flows for embedding provider {embedding_provider}"
1421
- )
1452
+ await flows_service.change_langflow_model_value(
1453
+ provider,
1454
+ embedding_model=embedding_model,
1455
+ force_embedding_update=True
1456
+ )
1457
+ logger.info(
1458
+ f"Successfully updated Langflow flows for embedding provider {provider}"
1459
+ )
1422
1460
 
1423
1461
  except Exception as e:
1424
1462
  logger.error(f"Failed to update Langflow model values: {str(e)}")
@@ -1514,13 +1552,13 @@ async def reapply_all_settings(session_manager = None):
1514
1552
  logger.info("Reapplying all settings to Langflow flows and global variables")
1515
1553
 
1516
1554
  if config.knowledge.embedding_model or config.knowledge.embedding_provider:
1517
- await _update_mcp_servers_with_provider_credentials(config, session_manager)
1555
+ await _update_mcp_servers_with_provider_credentials(config, session_manager, flows_service=flows_service)
1518
1556
  else:
1519
1557
  logger.info("No embedding model or provider configured, skipping MCP server update")
1520
1558
 
1521
1559
  # Update all Langflow settings using helper functions
1522
1560
  try:
1523
- await _update_langflow_global_variables(config)
1561
+ await _update_langflow_global_variables(config, flows_service=flows_service)
1524
1562
  except Exception as e:
1525
1563
  logger.error(f"Failed to update Langflow global variables: {str(e)}")
1526
1564
  # Continue with other updates even if global variables fail
@@ -1639,6 +1677,8 @@ async def rollback_onboarding(
1639
1677
  # Clear embedding provider and model settings
1640
1678
  current_config.knowledge.embedding_provider = "openai" # Reset to default
1641
1679
  current_config.knowledge.embedding_model = ""
1680
+ current_config.onboarding.openrag_docs_ingested_version = None
1681
+ current_config.onboarding.openrag_docs_remote_signature = None
1642
1682
 
1643
1683
  # Mark config as not edited so user can go through onboarding again
1644
1684
  current_config.edited = False
@@ -1756,3 +1796,38 @@ async def update_docling_preset(
1756
1796
  except Exception as e:
1757
1797
  logger.error("Failed to update docling settings", error=str(e))
1758
1798
  raise HTTPException(status_code=500, detail=f"Failed to update docling settings: {str(e)}")
1799
+
1800
+
1801
+ async def refresh_openrag_docs(
1802
+ document_service=Depends(get_document_service),
1803
+ task_service=Depends(get_task_service),
1804
+ langflow_file_service=Depends(get_langflow_file_service),
1805
+ session_manager=Depends(get_session_manager),
1806
+ user: User = Depends(get_current_user),
1807
+ ) -> RefreshOpenRAGDocsResponse:
1808
+ """Manually refresh OpenRAG docs ingestion on demand."""
1809
+ try:
1810
+ from main import refresh_default_openrag_docs
1811
+
1812
+ refreshed = await refresh_default_openrag_docs(
1813
+ document_service=document_service,
1814
+ task_service=task_service,
1815
+ langflow_file_service=langflow_file_service,
1816
+ session_manager=session_manager,
1817
+ force=True,
1818
+ reason="manual",
1819
+ )
1820
+ return RefreshOpenRAGDocsResponse(
1821
+ message=(
1822
+ "OpenRAG docs were refreshed."
1823
+ if refreshed
1824
+ else "OpenRAG docs refresh was skipped by current configuration."
1825
+ ),
1826
+ refreshed=refreshed,
1827
+ )
1828
+ except Exception as e:
1829
+ logger.error("Failed to refresh OpenRAG docs on demand", error=str(e))
1830
+ raise HTTPException(
1831
+ status_code=500,
1832
+ detail=f"Failed to refresh OpenRAG docs: {str(e)}",
1833
+ )
@@ -9,6 +9,7 @@ from typing import List, Optional
9
9
  from fastapi import Depends, File, Form, UploadFile
10
10
  from pydantic import BaseModel
11
11
  from fastapi.responses import JSONResponse
12
+ from api.documents import delete_documents_by_filename_core
12
13
 
13
14
  from api.router import upload_ingest_router
14
15
  from utils.logging_config import get_logger
@@ -83,31 +84,10 @@ async def delete_document_endpoint(
83
84
  user: User = Depends(get_api_key_user_async),
84
85
  ):
85
86
  """Delete a document from the knowledge base. DELETE /v1/documents"""
86
- filename = body.filename.strip()
87
- if not filename:
88
- return JSONResponse({"error": "Filename is required"}, status_code=400)
89
-
90
- try:
91
- from config.settings import get_index_name
92
- from utils.opensearch_queries import build_filename_delete_body
93
-
94
- opensearch_client = session_manager.get_user_opensearch_client(user.user_id, None)
95
- delete_query = build_filename_delete_body(filename)
96
-
97
- result = await opensearch_client.delete_by_query(
98
- index=get_index_name(),
99
- body=delete_query,
100
- conflicts="proceed",
101
- )
102
-
103
- deleted_count = result.get("deleted", 0)
104
- logger.info(f"Deleted {deleted_count} chunks for filename {filename}", user_id=user.user_id)
105
- return JSONResponse({"success": True, "deleted_chunks": deleted_count})
106
-
107
- except Exception as e:
108
- error_msg = str(e)
109
- logger.error("Document deletion failed", error=error_msg, filename=filename)
110
- if "AuthenticationException" in error_msg or "access denied" in error_msg.lower():
111
- return JSONResponse({"error": error_msg}, status_code=403)
112
- else:
113
- return JSONResponse({"error": error_msg}, status_code=500)
87
+ payload, status_code = await delete_documents_by_filename_core(
88
+ filename=body.filename,
89
+ session_manager=session_manager,
90
+ user_id=user.user_id,
91
+ jwt_token=None,
92
+ )
93
+ return JSONResponse(payload, status_code=status_code)
@@ -10,6 +10,7 @@ from fastapi import Depends
10
10
  from pydantic import BaseModel
11
11
  from fastapi.responses import JSONResponse
12
12
  from utils.logging_config import get_logger
13
+ from utils.opensearch_utils import OpenSearchDiskSpaceError, DISK_SPACE_ERROR_MESSAGE
13
14
  from dependencies import get_search_service, get_api_key_user_async
14
15
  from session_manager import User
15
16
 
@@ -65,6 +66,9 @@ async def search_endpoint(
65
66
 
66
67
  return JSONResponse({"results": results})
67
68
 
69
+ except OpenSearchDiskSpaceError as e:
70
+ logger.error("Search blocked by disk space constraint", error=str(e), user_id=user.user_id)
71
+ return JSONResponse({"error": DISK_SPACE_ERROR_MESSAGE}, status_code=507)
68
72
  except Exception as e:
69
73
  error_msg = str(e)
70
74
  logger.error("Search failed", error=error_msg, user_id=user.user_id)
@@ -37,6 +37,7 @@ class WatsonXConfig:
37
37
  class OllamaConfig:
38
38
  """Ollama provider configuration."""
39
39
  endpoint: str = ""
40
+ resolved_endpoint: str = ""
40
41
  configured: bool = False
41
42
 
42
43
 
@@ -97,6 +98,8 @@ class OnboardingState:
97
98
  upload_steps: Optional[Dict[str, Any]] = field(default=None)
98
99
  openrag_docs_filter_id: Optional[str] = field(default=None)
99
100
  user_doc_filter_id: Optional[str] = field(default=None)
101
+ openrag_docs_ingested_version: Optional[str] = field(default=None)
102
+ openrag_docs_remote_signature: Optional[str] = field(default=None)
100
103
 
101
104
 
102
105
  @dataclass
@@ -311,7 +314,7 @@ class ConfigManager:
311
314
  return True
312
315
  except Exception as e:
313
316
  logger.error(f"Failed to save configuration to {self.config_file}: {e}")
314
- return False
317
+ raise e
315
318
 
316
319
  def update_onboarding_state(self, **kwargs) -> bool:
317
320
  """Update onboarding state fields.