openrag 0.4.1.dev7__tar.gz → 0.4.1.dev9__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.1.dev7/src/openrag.egg-info → openrag-0.4.1.dev9}/PKG-INFO +1 -1
  2. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/pyproject.toml +1 -1
  3. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/agent.py +69 -21
  4. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/settings.py +1 -3
  5. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/main.py +42 -2
  6. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9/src/openrag.egg-info}/PKG-INFO +1 -1
  7. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/services/task_service.py +10 -0
  8. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/_assets/docker-compose.yml +13 -16
  9. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/utils/embeddings.py +1 -1
  10. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/utils/opensearch_utils.py +37 -0
  11. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/LICENSE +0 -0
  12. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/MANIFEST.in +0 -0
  13. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/README.md +0 -0
  14. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/setup.cfg +0 -0
  15. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/__init__.py +0 -0
  16. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/auth.py +0 -0
  17. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/chat.py +0 -0
  18. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/connector_router.py +0 -0
  19. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/connectors.py +0 -0
  20. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/docling.py +0 -0
  21. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/documents.py +0 -0
  22. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/flows.py +0 -0
  23. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/keys.py +0 -0
  24. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/knowledge_filter.py +0 -0
  25. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/langflow_files.py +0 -0
  26. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/models.py +0 -0
  27. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/nudges.py +0 -0
  28. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/oidc.py +0 -0
  29. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/provider_health.py +0 -0
  30. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/provider_validation.py +0 -0
  31. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/router.py +0 -0
  32. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/search.py +0 -0
  33. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/tasks.py +0 -0
  34. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/upload.py +0 -0
  35. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/v1/__init__.py +0 -0
  36. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/v1/chat.py +0 -0
  37. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/v1/documents.py +0 -0
  38. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/v1/knowledge_filters.py +0 -0
  39. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/v1/models.py +0 -0
  40. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/v1/search.py +0 -0
  41. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/api/v1/settings.py +0 -0
  42. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/auth_context.py +0 -0
  43. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/config/__init__.py +0 -0
  44. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/config/config_manager.py +0 -0
  45. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/config/model_constants.py +0 -0
  46. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/config/settings.py +0 -0
  47. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/__init__.py +0 -0
  48. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/aws_s3/__init__.py +0 -0
  49. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/aws_s3/api.py +0 -0
  50. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/aws_s3/auth.py +0 -0
  51. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/aws_s3/connector.py +0 -0
  52. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/aws_s3/models.py +0 -0
  53. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/aws_s3/support.py +0 -0
  54. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/base.py +0 -0
  55. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/connection_manager.py +0 -0
  56. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/google_drive/__init__.py +0 -0
  57. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/google_drive/connector.py +0 -0
  58. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/google_drive/oauth.py +0 -0
  59. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/ibm_cos/__init__.py +0 -0
  60. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/ibm_cos/api.py +0 -0
  61. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/ibm_cos/auth.py +0 -0
  62. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/ibm_cos/connector.py +0 -0
  63. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/ibm_cos/models.py +0 -0
  64. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/ibm_cos/support.py +0 -0
  65. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/langflow_connector_service.py +0 -0
  66. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/onedrive/__init__.py +0 -0
  67. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/onedrive/connector.py +0 -0
  68. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/onedrive/oauth.py +0 -0
  69. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/service.py +0 -0
  70. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/sharepoint/__init__.py +0 -0
  71. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/sharepoint/connector.py +0 -0
  72. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/sharepoint/oauth.py +0 -0
  73. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/connectors/sharepoint/utils.py +0 -0
  74. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/dependencies.py +0 -0
  75. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/models/__init__.py +0 -0
  76. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/models/processors.py +0 -0
  77. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/models/tasks.py +0 -0
  78. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/models/url.py +0 -0
  79. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/openrag.egg-info/SOURCES.txt +0 -0
  80. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/openrag.egg-info/dependency_links.txt +0 -0
  81. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/openrag.egg-info/entry_points.txt +0 -0
  82. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/openrag.egg-info/requires.txt +0 -0
  83. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/openrag.egg-info/top_level.txt +0 -0
  84. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/services/__init__.py +0 -0
  85. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/services/api_key_service.py +0 -0
  86. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/services/auth_service.py +0 -0
  87. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/services/chat_service.py +0 -0
  88. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/services/conversation_persistence_service.py +0 -0
  89. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/services/document_service.py +0 -0
  90. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/services/flows_service.py +0 -0
  91. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/services/knowledge_filter_service.py +0 -0
  92. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/services/langflow_file_service.py +0 -0
  93. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/services/langflow_history_service.py +0 -0
  94. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/services/langflow_mcp_service.py +0 -0
  95. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/services/models_service.py +0 -0
  96. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/services/monitor_service.py +0 -0
  97. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/services/search_service.py +0 -0
  98. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/services/session_ownership_service.py +0 -0
  99. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/session_manager.py +0 -0
  100. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/__init__.py +0 -0
  101. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/_assets/docker-compose.gpu.yml +0 -0
  102. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/_assets/flows/components/ollama_embedding.json +0 -0
  103. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/_assets/flows/components/ollama_llm.json +0 -0
  104. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/_assets/flows/components/ollama_llm_text.json +0 -0
  105. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/_assets/flows/components/watsonx_embedding.json +0 -0
  106. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/_assets/flows/components/watsonx_llm.json +0 -0
  107. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/_assets/flows/components/watsonx_llm_text.json +0 -0
  108. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/_assets/flows/ingestion_flow.json +0 -0
  109. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/_assets/flows/openrag_agent.json +0 -0
  110. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/_assets/flows/openrag_nudges.json +0 -0
  111. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/_assets/flows/openrag_url_mcp.json +0 -0
  112. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/_assets/openrag-documents/docling.pdf +0 -0
  113. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/_assets/openrag-documents/ibm_anthropic.pdf +0 -0
  114. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/_assets/openrag-documents/openrag-documentation.pdf +0 -0
  115. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/_assets/openrag-documents/warmup_ocr.pdf +0 -0
  116. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/cli.py +0 -0
  117. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/config_fields.py +0 -0
  118. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/main.py +0 -0
  119. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/managers/__init__.py +0 -0
  120. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/managers/container_manager.py +0 -0
  121. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/managers/docling_manager.py +0 -0
  122. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/managers/env_manager.py +0 -0
  123. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/screens/__init__.py +0 -0
  124. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/screens/config.py +0 -0
  125. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/screens/diagnostics.py +0 -0
  126. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/screens/logs.py +0 -0
  127. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/screens/monitor.py +0 -0
  128. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/screens/welcome.py +0 -0
  129. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/utils/__init__.py +0 -0
  130. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/utils/clipboard.py +0 -0
  131. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/utils/platform.py +0 -0
  132. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/utils/startup_checks.py +0 -0
  133. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/utils/validation.py +0 -0
  134. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/utils/version_check.py +0 -0
  135. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/widgets/__init__.py +0 -0
  136. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/widgets/command_modal.py +0 -0
  137. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/widgets/diagnostics_notification.py +0 -0
  138. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/widgets/error_notification.py +0 -0
  139. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/widgets/factory_reset_warning_modal.py +0 -0
  140. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/widgets/flow_backup_warning_modal.py +0 -0
  141. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/widgets/prune_options_modal.py +0 -0
  142. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/widgets/upgrade_instructions_modal.py +0 -0
  143. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/widgets/version_mismatch_warning_modal.py +0 -0
  144. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/tui/widgets/waves.py +0 -0
  145. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/utils/__init__.py +0 -0
  146. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/utils/acl_utils.py +0 -0
  147. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/utils/container_utils.py +0 -0
  148. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/utils/docling_client.py +0 -0
  149. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/utils/document_processing.py +0 -0
  150. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/utils/embedding_fields.py +0 -0
  151. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/utils/env_utils.py +0 -0
  152. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/utils/file_utils.py +0 -0
  153. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/utils/gpu_detection.py +0 -0
  154. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/utils/hash_utils.py +0 -0
  155. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/utils/langflow_headers.py +0 -0
  156. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/utils/langflow_utils.py +0 -0
  157. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/utils/logging_config.py +0 -0
  158. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/utils/opensearch_queries.py +0 -0
  159. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/utils/paths.py +0 -0
  160. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/utils/telemetry/__init__.py +0 -0
  161. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/utils/telemetry/category.py +0 -0
  162. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/utils/telemetry/client.py +0 -0
  163. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/src/utils/telemetry/message_id.py +0 -0
  164. {openrag-0.4.1.dev7 → openrag-0.4.1.dev9}/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.1.dev7
3
+ Version: 0.4.1.dev9
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "openrag"
7
- version = "0.4.1.dev7"
7
+ version = "0.4.1.dev9"
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"
@@ -158,7 +158,7 @@ async def async_response_stream(
158
158
  else:
159
159
  delta_text = str(chunk.delta)
160
160
  full_response += delta_text
161
-
161
+
162
162
  # Enhanced logging for tool call detection (Granite 3.3 8b investigation)
163
163
  chunk_attrs = dir(chunk) if hasattr(chunk, '__dict__') else []
164
164
  tool_related_attrs = [attr for attr in chunk_attrs if 'tool' in attr.lower() or 'call' in attr.lower() or 'retrieval' in attr.lower()]
@@ -180,7 +180,7 @@ async def async_response_stream(
180
180
  chunk_data = chunk.__dict__
181
181
  else:
182
182
  chunk_data = str(chunk)
183
-
183
+
184
184
  # Log detailed chunk structure for investigation (especially for Granite 3.3 8b)
185
185
  if isinstance(chunk_data, dict):
186
186
  # Check for any fields that might indicate tool usage
@@ -218,7 +218,7 @@ async def async_response_stream(
218
218
  'retrieved_documents' in chunk_data,
219
219
  'retrieval_results' in chunk_data,
220
220
  ])
221
-
221
+
222
222
  if has_results:
223
223
  logger.info(
224
224
  "Detected implicit tool call in backend, injecting synthetic event",
@@ -242,7 +242,7 @@ async def async_response_stream(
242
242
  # Send the synthetic event first
243
243
  yield (json.dumps(synthetic_event, default=str) + "\n").encode("utf-8")
244
244
  detected_tool_call = True # Mark that we've injected a tool call
245
-
245
+
246
246
  yield (json.dumps(chunk_data, default=str) + "\n").encode("utf-8")
247
247
  except Exception as e:
248
248
  # Fallback to string representation
@@ -626,20 +626,68 @@ async def async_langflow_chat(
626
626
 
627
627
  # Extract sources from retrieval tool calls in the response
628
628
  sources = []
629
+
630
+ # Layer 1: Structured output items (OpenAI Responses API format).
631
+ # Relaxed: check for any output item with a non-empty `results` field,
632
+ # regardless of `type` string (Langflow may use different type names).
629
633
  if hasattr(response_obj, "output") and response_obj.output:
630
634
  for output_item in response_obj.output:
631
- item_type = getattr(output_item, "type", None)
632
- if item_type in ("tool_call", "retrieval_call"):
633
- for result in getattr(output_item, "results", None) or []:
634
- rd = result.model_dump() if hasattr(result, "model_dump") else (result if isinstance(result, dict) else {})
635
- if "text" in rd:
636
- sources.append({
637
- "filename": rd.get("filename", ""),
638
- "text": rd.get("text", ""),
639
- "score": rd.get("score", 0),
640
- "page": rd.get("page"),
641
- "mimetype": rd.get("mimetype"),
642
- })
635
+ for result in getattr(output_item, "results", None) or []:
636
+ rd = (
637
+ result.model_dump()
638
+ if hasattr(result, "model_dump")
639
+ else (result if isinstance(result, dict) else {})
640
+ )
641
+ if "text" in rd:
642
+ sources.append({
643
+ "filename": rd.get("filename", ""),
644
+ "text": rd.get("text", ""),
645
+ "score": rd.get("score", 0),
646
+ "page": rd.get("page"),
647
+ "mimetype": rd.get("mimetype"),
648
+ })
649
+
650
+ # Layer 2: Top-level dict inspection (mirrors streaming middleware in async_response_stream).
651
+ # Langflow may embed retrieval results directly in the response dict rather than
652
+ # inside typed output items.
653
+ if not sources:
654
+ resp_dict = (
655
+ response_obj.model_dump()
656
+ if hasattr(response_obj, "model_dump")
657
+ else getattr(response_obj, "__dict__", {})
658
+ )
659
+ implicit_results = (
660
+ resp_dict.get("results")
661
+ or resp_dict.get("outputs")
662
+ or resp_dict.get("retrieved_documents")
663
+ or resp_dict.get("retrieval_results")
664
+ or []
665
+ )
666
+ if isinstance(implicit_results, list):
667
+ for result in implicit_results:
668
+ if isinstance(result, dict) and "text" in result:
669
+ sources.append({
670
+ "filename": result.get("filename", ""),
671
+ "text": result.get("text", ""),
672
+ "score": result.get("score", 0),
673
+ "page": result.get("page"),
674
+ "mimetype": result.get("mimetype"),
675
+ })
676
+
677
+ # Layer 3: Citation-text fallback.
678
+ # Parse "(Source: filename)" patterns emitted by the LLM when it cites documents.
679
+ # This is the last-resort fallback when Langflow's response object carries no
680
+ # structured retrieval data.
681
+ if not sources:
682
+ import re
683
+ for match in re.finditer(r"\(Source:\s*([^\)]+)\)", response_text):
684
+ sources.append({
685
+ "filename": match.group(1).strip(),
686
+ "text": "",
687
+ "score": 0,
688
+ "page": None,
689
+ "mimetype": None,
690
+ })
643
691
 
644
692
  if not store_conversation:
645
693
  return response_text, response_id, sources
@@ -739,7 +787,7 @@ async def async_langflow_chat_stream(
739
787
  response_id = chunk_data["id"]
740
788
  elif "response_id" in chunk_data:
741
789
  response_id = chunk_data["response_id"]
742
-
790
+
743
791
  # Check for error status
744
792
  if chunk_data.get("finish_reason") == "error" or chunk_data.get("status") == "failed":
745
793
  error_occurred = True
@@ -788,7 +836,7 @@ async def async_langflow_chat_stream(
788
836
  # Log the error
789
837
  logger.error(f"Error in langflow chat stream: {e}", exc_info=True)
790
838
  error_occurred = True
791
-
839
+
792
840
  # Store error message in conversation history so it persists
793
841
  error_message = {
794
842
  "role": "assistant",
@@ -797,19 +845,19 @@ async def async_langflow_chat_stream(
797
845
  "error": True,
798
846
  }
799
847
  conversation_state["messages"].append(error_message)
800
-
848
+
801
849
  # Try to store the conversation with error message
802
850
  # Use a temporary response_id if we don't have one
803
851
  if not response_id:
804
852
  response_id = f"error_{user_id}_{int(datetime.now().timestamp())}"
805
-
853
+
806
854
  try:
807
855
  conversation_state["last_activity"] = datetime.now()
808
856
  await store_conversation_thread(user_id, response_id, conversation_state)
809
857
  logger.debug(f"Stored conversation with error for user {user_id}")
810
858
  except Exception as store_error:
811
859
  logger.error(f"Failed to store error conversation: {store_error}")
812
-
860
+
813
861
  # Re-raise the exception so it propagates to the API layer
814
862
  raise
815
863
 
@@ -1664,8 +1664,6 @@ async def rollback_onboarding(
1664
1664
  {"error": "No onboarding configuration to rollback"}, status_code=400
1665
1665
  )
1666
1666
 
1667
- jwt_token = user.jwt_token
1668
-
1669
1667
  logger.info("Rolling back onboarding configuration due to file failures")
1670
1668
 
1671
1669
  # Get all tasks for the user
@@ -1728,7 +1726,7 @@ async def rollback_onboarding(
1728
1726
  if filename:
1729
1727
  try:
1730
1728
  opensearch_client = session_manager.get_user_opensearch_client(
1731
- user.user_id, jwt_token
1729
+ user.user_id, user.jwt_token
1732
1730
  )
1733
1731
  from utils.opensearch_queries import build_filename_delete_body
1734
1732
  from config.settings import get_index_name
@@ -248,10 +248,23 @@ async def init_index():
248
248
  )
249
249
  else:
250
250
  logger.info(
251
- "Index already exists, skipping creation",
251
+ "Index already exists, skipping creation and changing number of replicas",
252
252
  index_name=index_name,
253
253
  embedding_model=embedding_model,
254
254
  )
255
+ # Set number of replicas to 0 to not create unused nodes in OpenSearch, in case it was created with more replicas
256
+ current = await clients.opensearch.indices.get_settings(index=index_name)
257
+ current_replicas = int(
258
+ current[index_name]["settings"]["index"].get("number_of_replicas", 1)
259
+ )
260
+ if current_replicas != 0:
261
+ await clients.opensearch.indices.put_settings(
262
+ index=index_name,
263
+ body={"index": {"number_of_replicas": 0}},
264
+ )
265
+ logger.info(
266
+ "Updated documents index settings",
267
+ )
255
268
  await TelemetryClient.send_event(
256
269
  Category.OPENSEARCH_INDEX, MessageId.ORB_OS_INDEX_EXISTS
257
270
  )
@@ -259,6 +272,9 @@ async def init_index():
259
272
  # Create knowledge filters index
260
273
  knowledge_filter_index_name = "knowledge_filters"
261
274
  knowledge_filter_index_body = {
275
+ "settings": {
276
+ "index": {"number_of_replicas": 0, "number_of_shards": 1},
277
+ },
262
278
  "mappings": {
263
279
  "properties": {
264
280
  "id": {"type": "keyword"},
@@ -272,7 +288,7 @@ async def init_index():
272
288
  "created_at": {"type": "date"},
273
289
  "updated_at": {"type": "date"},
274
290
  }
275
- }
291
+ },
276
292
  }
277
293
 
278
294
  if not await clients.opensearch.indices.exists(
@@ -294,6 +310,19 @@ async def init_index():
294
310
  index_name=knowledge_filter_index_name,
295
311
  )
296
312
 
313
+ current = await clients.opensearch.indices.get_settings(index=knowledge_filter_index_name)
314
+ current_replicas = int(
315
+ current[knowledge_filter_index_name]["settings"]["index"].get("number_of_replicas", 1)
316
+ )
317
+ if current_replicas != 0:
318
+ await clients.opensearch.indices.put_settings(
319
+ index=knowledge_filter_index_name,
320
+ body={"index": {"number_of_replicas": 0}},
321
+ )
322
+ logger.info(
323
+ "Updated knowledge filters index settings",
324
+ )
325
+
297
326
  # Create API keys index for public API authentication
298
327
  if not await clients.opensearch.indices.exists(index=API_KEYS_INDEX_NAME):
299
328
  await clients.opensearch.indices.create(
@@ -1861,6 +1890,16 @@ async def create_app():
1861
1890
  await TelemetryClient.send_event(
1862
1891
  Category.APPLICATION_SHUTDOWN, MessageId.ORB_APP_SHUTDOWN
1863
1892
  )
1893
+ logger.info("Application shutdown initiated")
1894
+
1895
+ # Gracefully shutdown OpenSearch connection first
1896
+ try:
1897
+ from utils.opensearch_utils import graceful_opensearch_shutdown
1898
+ await graceful_opensearch_shutdown(clients.opensearch)
1899
+ clients.opensearch = None # prevent double-close in clients.cleanup()
1900
+ except Exception as e:
1901
+ logger.error("Error during graceful OpenSearch shutdown", error=str(e))
1902
+
1864
1903
  await cleanup_subscriptions_proper(services)
1865
1904
  # Cleanup task service (cancels background tasks and process pool)
1866
1905
  await services["task_service"].shutdown()
@@ -1870,6 +1909,7 @@ async def create_app():
1870
1909
  from utils.telemetry.client import cleanup_telemetry_client
1871
1910
 
1872
1911
  await cleanup_telemetry_client()
1912
+ logger.info("Application shutdown completed")
1873
1913
 
1874
1914
  return app
1875
1915
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openrag
3
- Version: 0.4.1.dev7
3
+ Version: 0.4.1.dev9
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
@@ -411,6 +411,16 @@ class TaskService:
411
411
 
412
412
  # Mark task as completed if all files (including appended ones) are done
413
413
  if upload_task.processed_files >= upload_task.total_files:
414
+ # Force an index refresh BEFORE marking the task COMPLETED so that
415
+ # callers polling for COMPLETED status can immediately query or delete
416
+ # the newly indexed chunks without hitting the near-real-time refresh window.
417
+ if upload_task.successful_files > 0:
418
+ try:
419
+ from config.settings import clients, get_index_name
420
+ await clients.opensearch.indices.refresh(index=get_index_name())
421
+ except Exception as e:
422
+ logger.debug("Index refresh after ingest failed (non-fatal)", error=str(e))
423
+
414
424
  upload_task.status = TaskStatus.COMPLETED
415
425
  upload_task.updated_at = time.time()
416
426
 
@@ -10,26 +10,20 @@ services:
10
10
  environment:
11
11
  - discovery.type=single-node
12
12
  - OPENSEARCH_INITIAL_ADMIN_PASSWORD=${OPENSEARCH_PASSWORD}
13
- # Run security setup in background after OpenSearch starts
14
- command: >
15
- bash -c "
16
- # Ensure data directory has correct permissions
17
- sudo chown -R opensearch:opensearch /usr/share/opensearch/data || true
18
-
19
- # Start OpenSearch in background
20
- /usr/share/opensearch/opensearch-docker-entrypoint.sh opensearch &
21
-
22
- # Wait a bit for OpenSearch to start, then apply security config
23
- sleep 10 && /usr/share/opensearch/setup-security.sh &
24
-
25
- # Wait for background processes
26
- wait
27
- "
13
+ - OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g
28
14
  ports:
29
15
  - "9200:9200"
30
16
  - "9600:9600"
31
17
  volumes:
32
- - ${OPENSEARCH_DATA_PATH:-./opensearch-data}:/usr/share/opensearch/data:U,z
18
+ - opensearch-data:/usr/share/opensearch/data
19
+ stop_grace_period: 2m
20
+ healthcheck:
21
+ test: ["CMD-SHELL", "curl -ku admin:$$OPENSEARCH_PASSWORD 'https://localhost:9200/_cluster/health' -s | grep -qE '\"status\":\"(green|yellow)\"'"]
22
+ interval: 15s
23
+ timeout: 10s
24
+ retries: 20
25
+ start_period: 60s
26
+ restart: unless-stopped
33
27
 
34
28
  dashboards:
35
29
  image: opensearchproject/opensearch-dashboards:3.0.0
@@ -186,3 +180,6 @@ services:
186
180
  - LANGFLOW_ENABLE_SUPERUSER_CLI=${LANGFLOW_ENABLE_SUPERUSER_CLI}
187
181
  # - DEFAULT_FOLDER_NAME=OpenRAG
188
182
  - HIDE_GETTING_STARTED_PROGRESS=true
183
+
184
+ volumes:
185
+ opensearch-data:
@@ -158,7 +158,7 @@ async def create_dynamic_index_body(
158
158
  "settings": {
159
159
  "index": {"knn": True},
160
160
  "number_of_shards": 1,
161
- "number_of_replicas": 1,
161
+ "number_of_replicas": 0
162
162
  },
163
163
  "mappings": {
164
164
  "properties": {
@@ -121,3 +121,40 @@ async def wait_for_opensearch(
121
121
  message: str = "Failed to verify whether OpenSearch is ready."
122
122
  logger.error(message)
123
123
  raise OpenSearchNotReadyError(message)
124
+
125
+
126
+ async def graceful_opensearch_shutdown(opensearch_client: AsyncOpenSearch) -> None:
127
+ """Gracefully shutdown OpenSearch client connection.
128
+
129
+ This ensures that all pending operations are completed and connections
130
+ are properly closed before the application exits.
131
+
132
+ Args:
133
+ opensearch_client: The OpenSearch client to shutdown.
134
+ """
135
+ if opensearch_client is None:
136
+ logger.debug("OpenSearch client is None, skipping graceful shutdown")
137
+ return
138
+
139
+ try:
140
+ logger.info("Initiating graceful OpenSearch shutdown...")
141
+
142
+ # Flush any pending write operations before closing
143
+ try:
144
+ await asyncio.wait_for(
145
+ opensearch_client.indices.flush(index="_all", wait_if_ongoing=True),
146
+ timeout=10.0
147
+ )
148
+ logger.debug("Index flush completed")
149
+ except asyncio.TimeoutError:
150
+ logger.warning("Timeout during index flush")
151
+ except Exception as e:
152
+ logger.warning("Error during index flush", error=str(e))
153
+
154
+ # Close the client connection
155
+ await opensearch_client.close()
156
+ logger.info("OpenSearch client connection closed gracefully")
157
+
158
+ except Exception as e:
159
+ logger.error("Error during graceful OpenSearch shutdown", error=str(e))
160
+
File without changes
File without changes
File without changes
File without changes