OpenRAG 0.2.0__tar.gz → 0.2.1__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 (143) hide show
  1. {openrag-0.2.0/src/openrag.egg-info → openrag-0.2.1}/PKG-INFO +10 -58
  2. openrag-0.2.1/README.md +34 -0
  3. {openrag-0.2.0 → openrag-0.2.1}/pyproject.toml +1 -1
  4. {openrag-0.2.0 → openrag-0.2.1}/src/api/docling.py +10 -3
  5. {openrag-0.2.0 → openrag-0.2.1}/src/api/keys.py +13 -0
  6. {openrag-0.2.0 → openrag-0.2.1}/src/api/knowledge_filter.py +57 -2
  7. {openrag-0.2.0 → openrag-0.2.1}/src/api/oidc.py +11 -1
  8. {openrag-0.2.0 → openrag-0.2.1}/src/main.py +24 -0
  9. {openrag-0.2.0 → openrag-0.2.1/src/openrag.egg-info}/PKG-INFO +10 -58
  10. {openrag-0.2.0 → openrag-0.2.1}/src/services/models_service.py +0 -6
  11. {openrag-0.2.0 → openrag-0.2.1}/src/session_manager.py +40 -6
  12. openrag-0.2.1/src/tui/_assets/docker-compose.gpu.yml +12 -0
  13. {openrag-0.2.0 → openrag-0.2.1}/src/tui/_assets/docker-compose.yml +2 -2
  14. {openrag-0.2.0 → openrag-0.2.1}/src/tui/main.py +51 -1
  15. {openrag-0.2.0 → openrag-0.2.1}/src/tui/managers/docling_manager.py +3 -3
  16. {openrag-0.2.0 → openrag-0.2.1}/src/tui/widgets/flow_backup_warning_modal.py +5 -3
  17. {openrag-0.2.0 → openrag-0.2.1}/src/tui/widgets/version_mismatch_warning_modal.py +2 -1
  18. openrag-0.2.0/README.md +0 -82
  19. openrag-0.2.0/src/tui/_assets/docker-compose.gpu.yml +0 -7
  20. {openrag-0.2.0 → openrag-0.2.1}/LICENSE +0 -0
  21. {openrag-0.2.0 → openrag-0.2.1}/MANIFEST.in +0 -0
  22. {openrag-0.2.0 → openrag-0.2.1}/setup.cfg +0 -0
  23. {openrag-0.2.0 → openrag-0.2.1}/src/agent.py +0 -0
  24. {openrag-0.2.0 → openrag-0.2.1}/src/api/__init__.py +0 -0
  25. {openrag-0.2.0 → openrag-0.2.1}/src/api/auth.py +0 -0
  26. {openrag-0.2.0 → openrag-0.2.1}/src/api/chat.py +0 -0
  27. {openrag-0.2.0 → openrag-0.2.1}/src/api/connector_router.py +0 -0
  28. {openrag-0.2.0 → openrag-0.2.1}/src/api/connectors.py +0 -0
  29. {openrag-0.2.0 → openrag-0.2.1}/src/api/documents.py +0 -0
  30. {openrag-0.2.0 → openrag-0.2.1}/src/api/flows.py +0 -0
  31. {openrag-0.2.0 → openrag-0.2.1}/src/api/langflow_files.py +0 -0
  32. {openrag-0.2.0 → openrag-0.2.1}/src/api/models.py +0 -0
  33. {openrag-0.2.0 → openrag-0.2.1}/src/api/nudges.py +0 -0
  34. {openrag-0.2.0 → openrag-0.2.1}/src/api/provider_health.py +0 -0
  35. {openrag-0.2.0 → openrag-0.2.1}/src/api/provider_validation.py +0 -0
  36. {openrag-0.2.0 → openrag-0.2.1}/src/api/router.py +0 -0
  37. {openrag-0.2.0 → openrag-0.2.1}/src/api/search.py +0 -0
  38. {openrag-0.2.0 → openrag-0.2.1}/src/api/settings.py +0 -0
  39. {openrag-0.2.0 → openrag-0.2.1}/src/api/tasks.py +0 -0
  40. {openrag-0.2.0 → openrag-0.2.1}/src/api/upload.py +0 -0
  41. {openrag-0.2.0 → openrag-0.2.1}/src/api/v1/__init__.py +0 -0
  42. {openrag-0.2.0 → openrag-0.2.1}/src/api/v1/chat.py +0 -0
  43. {openrag-0.2.0 → openrag-0.2.1}/src/api/v1/documents.py +0 -0
  44. {openrag-0.2.0 → openrag-0.2.1}/src/api/v1/knowledge_filters.py +0 -0
  45. {openrag-0.2.0 → openrag-0.2.1}/src/api/v1/search.py +0 -0
  46. {openrag-0.2.0 → openrag-0.2.1}/src/api/v1/settings.py +0 -0
  47. {openrag-0.2.0 → openrag-0.2.1}/src/api_key_middleware.py +0 -0
  48. {openrag-0.2.0 → openrag-0.2.1}/src/auth_context.py +0 -0
  49. {openrag-0.2.0 → openrag-0.2.1}/src/auth_middleware.py +0 -0
  50. {openrag-0.2.0 → openrag-0.2.1}/src/config/__init__.py +0 -0
  51. {openrag-0.2.0 → openrag-0.2.1}/src/config/config_manager.py +0 -0
  52. {openrag-0.2.0 → openrag-0.2.1}/src/config/settings.py +0 -0
  53. {openrag-0.2.0 → openrag-0.2.1}/src/connectors/__init__.py +0 -0
  54. {openrag-0.2.0 → openrag-0.2.1}/src/connectors/base.py +0 -0
  55. {openrag-0.2.0 → openrag-0.2.1}/src/connectors/connection_manager.py +0 -0
  56. {openrag-0.2.0 → openrag-0.2.1}/src/connectors/google_drive/__init__.py +0 -0
  57. {openrag-0.2.0 → openrag-0.2.1}/src/connectors/google_drive/connector.py +0 -0
  58. {openrag-0.2.0 → openrag-0.2.1}/src/connectors/google_drive/oauth.py +0 -0
  59. {openrag-0.2.0 → openrag-0.2.1}/src/connectors/langflow_connector_service.py +0 -0
  60. {openrag-0.2.0 → openrag-0.2.1}/src/connectors/onedrive/__init__.py +0 -0
  61. {openrag-0.2.0 → openrag-0.2.1}/src/connectors/onedrive/connector.py +0 -0
  62. {openrag-0.2.0 → openrag-0.2.1}/src/connectors/onedrive/oauth.py +0 -0
  63. {openrag-0.2.0 → openrag-0.2.1}/src/connectors/service.py +0 -0
  64. {openrag-0.2.0 → openrag-0.2.1}/src/connectors/sharepoint/__init__.py +0 -0
  65. {openrag-0.2.0 → openrag-0.2.1}/src/connectors/sharepoint/connector.py +0 -0
  66. {openrag-0.2.0 → openrag-0.2.1}/src/connectors/sharepoint/oauth.py +0 -0
  67. {openrag-0.2.0 → openrag-0.2.1}/src/models/__init__.py +0 -0
  68. {openrag-0.2.0 → openrag-0.2.1}/src/models/processors.py +0 -0
  69. {openrag-0.2.0 → openrag-0.2.1}/src/models/tasks.py +0 -0
  70. {openrag-0.2.0 → openrag-0.2.1}/src/openrag.egg-info/SOURCES.txt +0 -0
  71. {openrag-0.2.0 → openrag-0.2.1}/src/openrag.egg-info/dependency_links.txt +0 -0
  72. {openrag-0.2.0 → openrag-0.2.1}/src/openrag.egg-info/entry_points.txt +0 -0
  73. {openrag-0.2.0 → openrag-0.2.1}/src/openrag.egg-info/requires.txt +0 -0
  74. {openrag-0.2.0 → openrag-0.2.1}/src/openrag.egg-info/top_level.txt +0 -0
  75. {openrag-0.2.0 → openrag-0.2.1}/src/services/__init__.py +0 -0
  76. {openrag-0.2.0 → openrag-0.2.1}/src/services/api_key_service.py +0 -0
  77. {openrag-0.2.0 → openrag-0.2.1}/src/services/auth_service.py +0 -0
  78. {openrag-0.2.0 → openrag-0.2.1}/src/services/chat_service.py +0 -0
  79. {openrag-0.2.0 → openrag-0.2.1}/src/services/conversation_persistence_service.py +0 -0
  80. {openrag-0.2.0 → openrag-0.2.1}/src/services/document_service.py +0 -0
  81. {openrag-0.2.0 → openrag-0.2.1}/src/services/flows_service.py +0 -0
  82. {openrag-0.2.0 → openrag-0.2.1}/src/services/knowledge_filter_service.py +0 -0
  83. {openrag-0.2.0 → openrag-0.2.1}/src/services/langflow_file_service.py +0 -0
  84. {openrag-0.2.0 → openrag-0.2.1}/src/services/langflow_history_service.py +0 -0
  85. {openrag-0.2.0 → openrag-0.2.1}/src/services/langflow_mcp_service.py +0 -0
  86. {openrag-0.2.0 → openrag-0.2.1}/src/services/monitor_service.py +0 -0
  87. {openrag-0.2.0 → openrag-0.2.1}/src/services/search_service.py +0 -0
  88. {openrag-0.2.0 → openrag-0.2.1}/src/services/session_ownership_service.py +0 -0
  89. {openrag-0.2.0 → openrag-0.2.1}/src/services/task_service.py +0 -0
  90. {openrag-0.2.0 → openrag-0.2.1}/src/tui/__init__.py +0 -0
  91. {openrag-0.2.0 → openrag-0.2.1}/src/tui/_assets/flows/components/ollama_embedding.json +0 -0
  92. {openrag-0.2.0 → openrag-0.2.1}/src/tui/_assets/flows/components/ollama_llm.json +0 -0
  93. {openrag-0.2.0 → openrag-0.2.1}/src/tui/_assets/flows/components/ollama_llm_text.json +0 -0
  94. {openrag-0.2.0 → openrag-0.2.1}/src/tui/_assets/flows/components/watsonx_embedding.json +0 -0
  95. {openrag-0.2.0 → openrag-0.2.1}/src/tui/_assets/flows/components/watsonx_llm.json +0 -0
  96. {openrag-0.2.0 → openrag-0.2.1}/src/tui/_assets/flows/components/watsonx_llm_text.json +0 -0
  97. {openrag-0.2.0 → openrag-0.2.1}/src/tui/_assets/flows/ingestion_flow.json +0 -0
  98. {openrag-0.2.0 → openrag-0.2.1}/src/tui/_assets/flows/openrag_agent.json +0 -0
  99. {openrag-0.2.0 → openrag-0.2.1}/src/tui/_assets/flows/openrag_nudges.json +0 -0
  100. {openrag-0.2.0 → openrag-0.2.1}/src/tui/_assets/flows/openrag_url_mcp.json +0 -0
  101. {openrag-0.2.0 → openrag-0.2.1}/src/tui/_assets/openrag-documents/docling.pdf +0 -0
  102. {openrag-0.2.0 → openrag-0.2.1}/src/tui/_assets/openrag-documents/ibm_anthropic.pdf +0 -0
  103. {openrag-0.2.0 → openrag-0.2.1}/src/tui/_assets/openrag-documents/openrag-documentation.pdf +0 -0
  104. {openrag-0.2.0 → openrag-0.2.1}/src/tui/_assets/openrag-documents/warmup_ocr.pdf +0 -0
  105. {openrag-0.2.0 → openrag-0.2.1}/src/tui/managers/__init__.py +0 -0
  106. {openrag-0.2.0 → openrag-0.2.1}/src/tui/managers/container_manager.py +0 -0
  107. {openrag-0.2.0 → openrag-0.2.1}/src/tui/managers/env_manager.py +0 -0
  108. {openrag-0.2.0 → openrag-0.2.1}/src/tui/screens/__init__.py +0 -0
  109. {openrag-0.2.0 → openrag-0.2.1}/src/tui/screens/config.py +0 -0
  110. {openrag-0.2.0 → openrag-0.2.1}/src/tui/screens/diagnostics.py +0 -0
  111. {openrag-0.2.0 → openrag-0.2.1}/src/tui/screens/logs.py +0 -0
  112. {openrag-0.2.0 → openrag-0.2.1}/src/tui/screens/monitor.py +0 -0
  113. {openrag-0.2.0 → openrag-0.2.1}/src/tui/screens/welcome.py +0 -0
  114. {openrag-0.2.0 → openrag-0.2.1}/src/tui/utils/__init__.py +0 -0
  115. {openrag-0.2.0 → openrag-0.2.1}/src/tui/utils/clipboard.py +0 -0
  116. {openrag-0.2.0 → openrag-0.2.1}/src/tui/utils/platform.py +0 -0
  117. {openrag-0.2.0 → openrag-0.2.1}/src/tui/utils/validation.py +0 -0
  118. {openrag-0.2.0 → openrag-0.2.1}/src/tui/utils/version_check.py +0 -0
  119. {openrag-0.2.0 → openrag-0.2.1}/src/tui/widgets/__init__.py +0 -0
  120. {openrag-0.2.0 → openrag-0.2.1}/src/tui/widgets/command_modal.py +0 -0
  121. {openrag-0.2.0 → openrag-0.2.1}/src/tui/widgets/diagnostics_notification.py +0 -0
  122. {openrag-0.2.0 → openrag-0.2.1}/src/tui/widgets/error_notification.py +0 -0
  123. {openrag-0.2.0 → openrag-0.2.1}/src/tui/widgets/factory_reset_warning_modal.py +0 -0
  124. {openrag-0.2.0 → openrag-0.2.1}/src/tui/widgets/prune_options_modal.py +0 -0
  125. {openrag-0.2.0 → openrag-0.2.1}/src/tui/widgets/upgrade_instructions_modal.py +0 -0
  126. {openrag-0.2.0 → openrag-0.2.1}/src/tui/widgets/waves.py +0 -0
  127. {openrag-0.2.0 → openrag-0.2.1}/src/utils/__init__.py +0 -0
  128. {openrag-0.2.0 → openrag-0.2.1}/src/utils/container_utils.py +0 -0
  129. {openrag-0.2.0 → openrag-0.2.1}/src/utils/document_processing.py +0 -0
  130. {openrag-0.2.0 → openrag-0.2.1}/src/utils/embedding_fields.py +0 -0
  131. {openrag-0.2.0 → openrag-0.2.1}/src/utils/embeddings.py +0 -0
  132. {openrag-0.2.0 → openrag-0.2.1}/src/utils/file_utils.py +0 -0
  133. {openrag-0.2.0 → openrag-0.2.1}/src/utils/gpu_detection.py +0 -0
  134. {openrag-0.2.0 → openrag-0.2.1}/src/utils/hash_utils.py +0 -0
  135. {openrag-0.2.0 → openrag-0.2.1}/src/utils/langflow_headers.py +0 -0
  136. {openrag-0.2.0 → openrag-0.2.1}/src/utils/logging_config.py +0 -0
  137. {openrag-0.2.0 → openrag-0.2.1}/src/utils/opensearch_queries.py +0 -0
  138. {openrag-0.2.0 → openrag-0.2.1}/src/utils/paths.py +0 -0
  139. {openrag-0.2.0 → openrag-0.2.1}/src/utils/process_pool.py +0 -0
  140. {openrag-0.2.0 → openrag-0.2.1}/src/utils/telemetry/__init__.py +0 -0
  141. {openrag-0.2.0 → openrag-0.2.1}/src/utils/telemetry/category.py +0 -0
  142. {openrag-0.2.0 → openrag-0.2.1}/src/utils/telemetry/client.py +0 -0
  143. {openrag-0.2.0 → openrag-0.2.1}/src/utils/telemetry/message_id.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openrag
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.13
6
6
  Description-Content-Type: text/markdown
@@ -51,68 +51,20 @@ OpenRAG is a comprehensive Retrieval-Augmented Generation platform that enables
51
51
 
52
52
  <a href="https://deepwiki.com/langflow-ai/openrag"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
53
53
 
54
- </div>
55
- <div align="center">
56
- <a href="#quickstart" style="color: #0366d6;">Quickstart</a> &nbsp;&nbsp;|&nbsp;&nbsp;
57
- <a href="#install-python-package" style="color: #0366d6;">Python package</a> &nbsp;&nbsp;|&nbsp;&nbsp;
58
- <a href="#docker-or-podman-installation" style="color: #0366d6;">Docker or Podman</a> &nbsp;&nbsp;|&nbsp;&nbsp;
59
- <a href="#development" style="color: #0366d6;">Development</a> &nbsp;&nbsp;|&nbsp;&nbsp;
60
- <a href="#troubleshooting" style="color: #0366d6;">Troubleshooting</a>
61
- </div>
62
-
63
- ## Quickstart
64
-
65
- To run OpenRAG without creating or modifying any project files, use `uvx`:
66
-
67
- ```bash
68
- uvx openrag
69
- ```
70
-
71
- This command runs OpenRAG without installing it to your project or globally.
72
-
73
- To run a specific version of OpenRAG, run `uvx --from openrag==VERSION openrag`.
74
-
75
- ## Install Python package
76
-
77
- To add the OpenRAG Python package to a Python project, use `uv`:
78
-
79
- 1. Create a new project with a virtual environment using `uv init`:
54
+ ## Install OpenRAG
80
55
 
81
- ```bash
82
- uv init YOUR_PROJECT_NAME
83
- cd YOUR_PROJECT_NAME
84
- ```
56
+ To get started with OpenRAG, see the installation guides in the OpenRAG documentation:
85
57
 
86
- The `(venv)` prompt doesn't change, but `uv` commands will automatically use the project's virtual environment.
87
- For more information on virtual environments, see the [uv documentation](https://docs.astral.sh/uv/pip/environments).
88
-
89
- 2. Add OpenRAG to your project:
90
-
91
- ```bash
92
- uv add openrag
93
- ```
94
-
95
- To add a specific version of OpenRAG, run `uv add openrag==VERSION`.
96
-
97
- 3. Start the OpenRAG terminal user interface (TUI):
98
-
99
- ```bash
100
- uv run openrag
101
- ```
102
-
103
- 4. Continue with the [Quickstart](https://docs.openr.ag/quickstart).
104
-
105
- For all installation options, see the [OpenRAG installation guide](https://docs.openr.ag/install).
106
-
107
- ## Docker or Podman installation
108
-
109
- By default, OpenRAG automatically starts the required containers and helps you manage them.
110
- To install OpenRAG with self-managed containers, see the [OpenRAG installation guide](https://docs.openr.ag/docker).
58
+ * [Quickstart](https://docs.openr.ag/quickstart)
59
+ * [Install the OpenRAG Python package](https://docs.openr.ag/install-options)
60
+ * [Deploy self-managed services with Docker or Podman](https://docs.openr.ag/docker)
111
61
 
112
62
  ## Development
113
63
 
114
- For developers wanting to contribute to OpenRAG or set up a development environment, see [CONTRIBUTING.md](CONTRIBUTING.md).
64
+ 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).
115
65
 
116
66
  ## Troubleshooting
117
67
 
118
- For common issues and fixes, see [Troubleshoot OpenRAG](https://docs.openr.ag/support/troubleshoot).
68
+ 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).
69
+
70
+ To report a bug or submit a feature request, visit the [Issues page](https://github.com/langflow-ai/openrag/issues).
@@ -0,0 +1,34 @@
1
+ <div align="center">
2
+
3
+ # OpenRAG
4
+
5
+ <div align="center">
6
+ <a href="https://github.com/langflow-ai/langflow"><img src="https://img.shields.io/badge/Langflow-1C1C1E?style=flat&logo=langflow" alt="Langflow"></a>
7
+ &nbsp;&nbsp;
8
+ <a href="https://github.com/opensearch-project/OpenSearch"><img src="https://img.shields.io/badge/OpenSearch-005EB8?style=flat&logo=opensearch&logoColor=white" alt="OpenSearch"></a>
9
+ &nbsp;&nbsp;
10
+ <a href="https://github.com/docling-project/docling"><img src="https://img.shields.io/badge/Docling-000000?style=flat" alt="Langflow"></a>
11
+ &nbsp;&nbsp;
12
+ </div>
13
+
14
+ OpenRAG is a comprehensive Retrieval-Augmented Generation platform that enables intelligent document search and AI-powered conversations. 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. Built with [Starlette](https://github.com/Kludex/starlette) and [Next.js](https://github.com/vercel/next.js). Powered by [OpenSearch](https://github.com/opensearch-project/OpenSearch), [Langflow](https://github.com/langflow-ai/langflow), and [Docling](https://github.com/docling-project/docling).
15
+
16
+ <a href="https://deepwiki.com/langflow-ai/openrag"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
17
+
18
+ ## Install OpenRAG
19
+
20
+ To get started with OpenRAG, see the installation guides in the OpenRAG documentation:
21
+
22
+ * [Quickstart](https://docs.openr.ag/quickstart)
23
+ * [Install the OpenRAG Python package](https://docs.openr.ag/install-options)
24
+ * [Deploy self-managed services with Docker or Podman](https://docs.openr.ag/docker)
25
+
26
+ ## Development
27
+
28
+ 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).
29
+
30
+ ## Troubleshooting
31
+
32
+ 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).
33
+
34
+ To report a bug or submit a feature request, visit the [Issues page](https://github.com/langflow-ai/openrag/issues).
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "openrag"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  description = "Add your description here"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -1,5 +1,6 @@
1
1
  """Docling service proxy endpoints."""
2
2
 
3
+ import os
3
4
  import socket
4
5
  import struct
5
6
  from pathlib import Path
@@ -73,9 +74,15 @@ def determine_docling_host() -> str:
73
74
  return "localhost"
74
75
 
75
76
 
76
- # Detect the host IP once at startup
77
- HOST_IP = determine_docling_host()
78
- DOCLING_SERVICE_URL = f"http://{HOST_IP}:5001"
77
+ # Use explicit URL if provided, otherwise auto-detect host
78
+ _docling_url_override = os.getenv("DOCLING_SERVE_URL")
79
+ if _docling_url_override:
80
+ DOCLING_SERVICE_URL = _docling_url_override.rstrip("/")
81
+ HOST_IP = _docling_url_override # For display in health responses
82
+ logger.info("Using DOCLING_SERVE_URL override: %s", DOCLING_SERVICE_URL)
83
+ else:
84
+ HOST_IP = determine_docling_host()
85
+ DOCLING_SERVICE_URL = f"http://{HOST_IP}:5001"
79
86
 
80
87
 
81
88
  async def health(request: Request) -> JSONResponse:
@@ -4,6 +4,8 @@ API Key management endpoints.
4
4
  These endpoints use JWT cookie authentication (for the UI) and allow users
5
5
  to create, list, and revoke their API keys for use with the public API.
6
6
  """
7
+ import json
8
+
7
9
  from starlette.requests import Request
8
10
  from starlette.responses import JSONResponse
9
11
  from utils.logging_config import get_logger
@@ -64,6 +66,17 @@ async def create_key_endpoint(request: Request, api_key_service):
64
66
 
65
67
  try:
66
68
  data = await request.json()
69
+ except json.JSONDecodeError:
70
+ return JSONResponse(
71
+ {
72
+ "success": False,
73
+ "error": "Invalid or missing JSON body",
74
+ "example": {"name": "My API Key"},
75
+ },
76
+ status_code=400,
77
+ )
78
+
79
+ try:
67
80
  name = data.get("name", "").strip()
68
81
 
69
82
  if not name:
@@ -8,6 +8,42 @@ from utils.logging_config import get_logger
8
8
  logger = get_logger(__name__)
9
9
 
10
10
 
11
+ def normalize_query_data(query_data: str | dict) -> str:
12
+ """
13
+ Normalize query_data to ensure all required fields exist with defaults.
14
+ This prevents frontend crashes when API-created filters have incomplete data.
15
+ """
16
+ # Parse if string
17
+ if isinstance(query_data, str):
18
+ try:
19
+ data = json.loads(query_data)
20
+ except json.JSONDecodeError:
21
+ data = {}
22
+ else:
23
+ data = query_data or {}
24
+
25
+ # Ensure filters object exists with all required fields
26
+ filters = data.get("filters") or {}
27
+ normalized_filters = {
28
+ "data_sources": filters.get("data_sources", ["*"]),
29
+ "document_types": filters.get("document_types", ["*"]),
30
+ "owners": filters.get("owners", ["*"]),
31
+ "connector_types": filters.get("connector_types", ["*"]),
32
+ }
33
+
34
+ # Build normalized query_data with defaults
35
+ normalized = {
36
+ "query": data.get("query", ""),
37
+ "filters": normalized_filters,
38
+ "limit": data.get("limit", 10),
39
+ "scoreThreshold": data.get("scoreThreshold", 0),
40
+ "color": data.get("color", "zinc"),
41
+ "icon": data.get("icon", "filter"),
42
+ }
43
+
44
+ return json.dumps(normalized)
45
+
46
+
11
47
  async def create_knowledge_filter(
12
48
  request: Request, knowledge_filter_service, session_manager
13
49
  ):
@@ -25,6 +61,15 @@ async def create_knowledge_filter(
25
61
  if not query_data:
26
62
  return JSONResponse({"error": "Query data is required"}, status_code=400)
27
63
 
64
+ # Normalize query_data to ensure all required fields exist
65
+ try:
66
+ normalized_query_data = normalize_query_data(query_data)
67
+ except Exception as e:
68
+ logger.error(f"Failed to normalize query_data: {e}")
69
+ return JSONResponse(
70
+ {"error": f"Invalid queryData format: {str(e)}"}, status_code=400
71
+ )
72
+
28
73
  user = request.state.user
29
74
  jwt_token = session_manager.get_effective_jwt_token(user.user_id, request.state.jwt_token)
30
75
 
@@ -34,7 +79,7 @@ async def create_knowledge_filter(
34
79
  "id": filter_id,
35
80
  "name": name,
36
81
  "description": description,
37
- "query_data": query_data, # Store the full search query JSON
82
+ "query_data": normalized_query_data, # Store normalized query JSON with defaults
38
83
  "owner": user.user_id,
39
84
  "allowed_users": payload.get("allowedUsers", []), # ACL field for future use
40
85
  "allowed_groups": payload.get("allowedGroups", []), # ACL field for future use
@@ -158,12 +203,22 @@ async def update_knowledge_filter(
158
203
  {"error": "Failed to delete existing knowledge filter"}, status_code=500
159
204
  )
160
205
 
206
+ # Normalize query_data if provided, otherwise use existing
207
+ query_data = payload.get("queryData", existing_filter["query_data"])
208
+ try:
209
+ normalized_query_data = normalize_query_data(query_data)
210
+ except Exception as e:
211
+ logger.error(f"Failed to normalize query_data: {e}")
212
+ return JSONResponse(
213
+ {"error": f"Invalid queryData format: {str(e)}"}, status_code=400
214
+ )
215
+
161
216
  # Create updated knowledge filter document with same ID
162
217
  updated_filter = {
163
218
  "id": filter_id,
164
219
  "name": payload.get("name", existing_filter["name"]),
165
220
  "description": payload.get("description", existing_filter["description"]),
166
- "query_data": payload.get("queryData", existing_filter["query_data"]),
221
+ "query_data": normalized_query_data,
167
222
  "owner": existing_filter["owner"],
168
223
  "allowed_users": payload.get(
169
224
  "allowedUsers", existing_filter.get("allowed_users", [])
@@ -1,3 +1,5 @@
1
+ import os
2
+
1
3
  from starlette.requests import Request
2
4
  from starlette.responses import JSONResponse
3
5
  import json
@@ -8,7 +10,11 @@ from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
8
10
 
9
11
  async def oidc_discovery(request: Request, session_manager):
10
12
  """OIDC discovery endpoint"""
11
- base_url = str(request.base_url).rstrip("/")
13
+ openrag_fqdn = os.getenv("OPENRAG_FQDN")
14
+ if openrag_fqdn:
15
+ base_url = f"http://{openrag_fqdn}:8000"
16
+ else:
17
+ base_url = str(request.base_url).rstrip("/")
12
18
 
13
19
  discovery_config = {
14
20
  "issuer": base_url,
@@ -44,6 +50,10 @@ async def jwks_endpoint(request: Request, session_manager):
44
50
  # Get the public key from session manager
45
51
  public_key_pem = session_manager.public_key_pem
46
52
 
53
+ if public_key_pem is None:
54
+ # Symmetric key in use - no JWKS available
55
+ return JSONResponse({"keys": []})
56
+
47
57
  # Parse the PEM to extract key components
48
58
  public_key = serialization.load_pem_public_key(public_key_pem.encode())
49
59
 
@@ -21,6 +21,7 @@ from functools import partial
21
21
 
22
22
  from starlette.applications import Starlette
23
23
  from starlette.routing import Route
24
+ from starlette.responses import JSONResponse
24
25
 
25
26
  # Set multiprocessing start method to 'spawn' for CUDA compatibility
26
27
  multiprocessing.set_start_method("spawn", force=True)
@@ -456,6 +457,24 @@ async def _ingest_default_documents_langflow(services, file_paths):
456
457
  file_count=len(file_paths),
457
458
  )
458
459
 
460
+ async def opensearch_health_ready(request):
461
+ """Readiness probe: verifies OpenSearch dependency is reachable."""
462
+ try:
463
+ # Fast check that the cluster is reachable/auth works
464
+ await asyncio.wait_for(clients.opensearch.info(), timeout=5.0)
465
+ return JSONResponse(
466
+ {"status": "ready", "dependencies": {"opensearch": "up"}},
467
+ status_code=200,
468
+ )
469
+ except Exception as e:
470
+ return JSONResponse(
471
+ {
472
+ "status": "not_ready",
473
+ "dependencies": {"opensearch": "down"},
474
+ "error": str(e),
475
+ },
476
+ status_code=503,
477
+ )
459
478
 
460
479
  async def _ingest_default_documents_openrag(services, file_paths):
461
480
  """Ingest default documents using traditional OpenRAG processor."""
@@ -1148,6 +1167,11 @@ async def create_app():
1148
1167
  ),
1149
1168
  methods=["GET"],
1150
1169
  ),
1170
+ Route(
1171
+ "/search/health",
1172
+ opensearch_health_ready,
1173
+ methods=["GET"],
1174
+ ),
1151
1175
  # Models endpoints
1152
1176
  Route(
1153
1177
  "/models/openai",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openrag
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.13
6
6
  Description-Content-Type: text/markdown
@@ -51,68 +51,20 @@ OpenRAG is a comprehensive Retrieval-Augmented Generation platform that enables
51
51
 
52
52
  <a href="https://deepwiki.com/langflow-ai/openrag"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
53
53
 
54
- </div>
55
- <div align="center">
56
- <a href="#quickstart" style="color: #0366d6;">Quickstart</a> &nbsp;&nbsp;|&nbsp;&nbsp;
57
- <a href="#install-python-package" style="color: #0366d6;">Python package</a> &nbsp;&nbsp;|&nbsp;&nbsp;
58
- <a href="#docker-or-podman-installation" style="color: #0366d6;">Docker or Podman</a> &nbsp;&nbsp;|&nbsp;&nbsp;
59
- <a href="#development" style="color: #0366d6;">Development</a> &nbsp;&nbsp;|&nbsp;&nbsp;
60
- <a href="#troubleshooting" style="color: #0366d6;">Troubleshooting</a>
61
- </div>
62
-
63
- ## Quickstart
64
-
65
- To run OpenRAG without creating or modifying any project files, use `uvx`:
66
-
67
- ```bash
68
- uvx openrag
69
- ```
70
-
71
- This command runs OpenRAG without installing it to your project or globally.
72
-
73
- To run a specific version of OpenRAG, run `uvx --from openrag==VERSION openrag`.
74
-
75
- ## Install Python package
76
-
77
- To add the OpenRAG Python package to a Python project, use `uv`:
78
-
79
- 1. Create a new project with a virtual environment using `uv init`:
54
+ ## Install OpenRAG
80
55
 
81
- ```bash
82
- uv init YOUR_PROJECT_NAME
83
- cd YOUR_PROJECT_NAME
84
- ```
56
+ To get started with OpenRAG, see the installation guides in the OpenRAG documentation:
85
57
 
86
- The `(venv)` prompt doesn't change, but `uv` commands will automatically use the project's virtual environment.
87
- For more information on virtual environments, see the [uv documentation](https://docs.astral.sh/uv/pip/environments).
88
-
89
- 2. Add OpenRAG to your project:
90
-
91
- ```bash
92
- uv add openrag
93
- ```
94
-
95
- To add a specific version of OpenRAG, run `uv add openrag==VERSION`.
96
-
97
- 3. Start the OpenRAG terminal user interface (TUI):
98
-
99
- ```bash
100
- uv run openrag
101
- ```
102
-
103
- 4. Continue with the [Quickstart](https://docs.openr.ag/quickstart).
104
-
105
- For all installation options, see the [OpenRAG installation guide](https://docs.openr.ag/install).
106
-
107
- ## Docker or Podman installation
108
-
109
- By default, OpenRAG automatically starts the required containers and helps you manage them.
110
- To install OpenRAG with self-managed containers, see the [OpenRAG installation guide](https://docs.openr.ag/docker).
58
+ * [Quickstart](https://docs.openr.ag/quickstart)
59
+ * [Install the OpenRAG Python package](https://docs.openr.ag/install-options)
60
+ * [Deploy self-managed services with Docker or Podman](https://docs.openr.ag/docker)
111
61
 
112
62
  ## Development
113
63
 
114
- For developers wanting to contribute to OpenRAG or set up a development environment, see [CONTRIBUTING.md](CONTRIBUTING.md).
64
+ 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).
115
65
 
116
66
  ## Troubleshooting
117
67
 
118
- For common issues and fixes, see [Troubleshoot OpenRAG](https://docs.openr.ag/support/troubleshoot).
68
+ 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).
69
+
70
+ To report a bug or submit a feature request, visit the [Issues page](https://github.com/langflow-ai/openrag/issues).
@@ -35,13 +35,7 @@ class ModelsService:
35
35
  "claude-opus-4-1-20250805",
36
36
  "claude-opus-4-20250514",
37
37
  "claude-sonnet-4-20250514",
38
- "claude-3-7-sonnet-latest",
39
- "claude-3-5-sonnet-latest",
40
38
  "claude-3-5-haiku-latest",
41
- "claude-3-opus-latest",
42
- "claude-3-sonnet-20240229",
43
- "claude-3-5-sonnet-20240620",
44
- "claude-3-5-sonnet-20241022",
45
39
  "claude-3-5-haiku-20241022",
46
40
  "claude-3-haiku-20240307",
47
41
  ]
@@ -60,13 +60,44 @@ class SessionManager:
60
60
  str, Any
61
61
  ] = {} # user_id -> OpenSearch client
62
62
 
63
- # Load RSA keys
64
63
  self.private_key_path = private_key_path
65
64
  self.public_key_path = public_key_path
66
- self._load_rsa_keys()
65
+
66
+ # Configure JWT signing (checks env var first, falls back to key files)
67
+ self._configure_jwt_signing()
68
+
69
+ def _configure_jwt_signing(self):
70
+ """Configure JWT signing - supports env var or file-based keys"""
71
+ signing_key = os.getenv("JWT_SIGNING_KEY")
72
+
73
+ if signing_key:
74
+ if signing_key.startswith("-----BEGIN"):
75
+ # PEM format = asymmetric (RS256)
76
+ self.private_key = serialization.load_pem_private_key(
77
+ signing_key.encode(), password=None
78
+ )
79
+ # Extract public key from private key
80
+ self.public_key = self.private_key.public_key()
81
+ self.public_key_pem = self.public_key.public_bytes(
82
+ encoding=serialization.Encoding.PEM,
83
+ format=serialization.PublicFormat.SubjectPublicKeyInfo
84
+ ).decode()
85
+ self.algorithm = "RS256"
86
+ logger.info("JWT signing configured with RSA key from environment")
87
+ else:
88
+ # Plain string = symmetric (HS256)
89
+ self.private_key = signing_key
90
+ self.public_key = signing_key # Same key for verification
91
+ self.public_key_pem = None # No JWKS for symmetric
92
+ self.algorithm = "HS256"
93
+ logger.info("JWT signing configured with symmetric key from environment")
94
+ else:
95
+ # Fall back to file-based RSA keys
96
+ self._load_rsa_keys()
97
+ self.algorithm = "RS256"
67
98
 
68
99
  def _load_rsa_keys(self):
69
- """Load RSA private and public keys"""
100
+ """Load RSA private and public keys from files"""
70
101
  try:
71
102
  with open(self.private_key_path, "rb") as f:
72
103
  self.private_key = serialization.load_pem_private_key(
@@ -76,7 +107,6 @@ class SessionManager:
76
107
  with open(self.public_key_path, "rb") as f:
77
108
  self.public_key = serialization.load_pem_public_key(f.read())
78
109
 
79
- # Also get public key in PEM format for JWKS
80
110
  self.public_key_pem = open(self.public_key_path, "r").read()
81
111
 
82
112
  except FileNotFoundError as e:
@@ -140,6 +170,9 @@ class SessionManager:
140
170
  """Create JWT token for an existing user"""
141
171
  # Use OpenSearch-compatible issuer for OIDC validation
142
172
  oidc_issuer = "http://openrag-backend:8000"
173
+ openrag_fqdn = os.getenv("OPENRAG_FQDN")
174
+ if openrag_fqdn:
175
+ oidc_issuer = f"http://{openrag_fqdn}:8000"
143
176
 
144
177
  # Create JWT token with OIDC-compliant claims
145
178
  now = datetime.utcnow()
@@ -158,9 +191,10 @@ class SessionManager:
158
191
  "preferred_username": user.email,
159
192
  "email_verified": True,
160
193
  "roles": ["openrag_user"], # Backend role for OpenSearch
194
+ "user_roles": ["openrag_user"], # compatible with OpenSearch's roles_key
161
195
  }
162
196
 
163
- token = jwt.encode(token_payload, self.private_key, algorithm="RS256")
197
+ token = jwt.encode(token_payload, self.private_key, algorithm=self.algorithm)
164
198
  return token
165
199
 
166
200
  def verify_token(self, token: str) -> Optional[Dict[str, Any]]:
@@ -169,7 +203,7 @@ class SessionManager:
169
203
  payload = jwt.decode(
170
204
  token,
171
205
  self.public_key,
172
- algorithms=["RS256"],
206
+ algorithms=[self.algorithm],
173
207
  audience=["opensearch", "openrag"],
174
208
  )
175
209
  return payload
@@ -0,0 +1,12 @@
1
+ services:
2
+ openrag-backend:
3
+ environment:
4
+ - NVIDIA_DRIVER_CAPABILITIES=compute,utility
5
+ - NVIDIA_VISIBLE_DEVICES=all
6
+ deploy:
7
+ resources:
8
+ reservations:
9
+ devices:
10
+ - driver: nvidia
11
+ count: all
12
+ capabilities: [gpu]
@@ -29,7 +29,7 @@ services:
29
29
  - "9200:9200"
30
30
  - "9600:9600"
31
31
  volumes:
32
- - ${OPENSEARCH_DATA_PATH:-./opensearch-data}:/usr/share/opensearch/data:Z
32
+ - ${OPENSEARCH_DATA_PATH:-./opensearch-data}:/usr/share/opensearch/data:U,z
33
33
 
34
34
  dashboards:
35
35
  image: opensearchproject/opensearch-dashboards:3.0.0
@@ -81,7 +81,7 @@ services:
81
81
  - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
82
82
  volumes:
83
83
  - ${OPENRAG_DOCUMENTS_PATH:-./openrag-documents}:/app/openrag-documents:Z
84
- - ${OPENRAG_KEYS_PATH:-./keys}:/app/keys:Z
84
+ - ${OPENRAG_KEYS_PATH:-./keys}:/app/keys:U,z
85
85
  - ${OPENRAG_FLOWS_PATH:-./flows}:/app/flows:U,z
86
86
  - ${OPENRAG_CONFIG_PATH:-./config}:/app/config:Z
87
87
  - ${OPENRAG_DATA_PATH:-./data}:/app/data:Z
@@ -1,5 +1,7 @@
1
1
  """Main TUI application for OpenRAG."""
2
2
 
3
+ import os
4
+ import subprocess
3
5
  import sys
4
6
  from pathlib import Path
5
7
  from typing import Iterable, Optional
@@ -683,6 +685,51 @@ def migrate_legacy_data_directories():
683
685
  logger.info("Data migration completed successfully")
684
686
 
685
687
 
688
+ def generate_jwt_keys(keys_dir: Path):
689
+ """Generate RSA keys for JWT signing if they don't exist.
690
+
691
+ This pre-generates keys on the host so containers can read them,
692
+ avoiding permission issues with Podman rootless mode.
693
+ """
694
+ private_key_path = keys_dir / "private_key.pem"
695
+ public_key_path = keys_dir / "public_key.pem"
696
+
697
+ if private_key_path.exists() and public_key_path.exists():
698
+ logger.debug("JWT keys already exist")
699
+ return
700
+
701
+ try:
702
+ # Generate private key
703
+ subprocess.run(
704
+ ["openssl", "genrsa", "-out", str(private_key_path), "2048"],
705
+ check=True,
706
+ capture_output=True,
707
+ )
708
+ # Set restrictive permissions on private key (readable by owner only)
709
+ os.chmod(private_key_path, 0o600)
710
+
711
+ # Generate public key from private key
712
+ subprocess.run(
713
+ [
714
+ "openssl",
715
+ "rsa",
716
+ "-in", str(private_key_path),
717
+ "-pubout",
718
+ "-out", str(public_key_path),
719
+ ],
720
+ check=True,
721
+ capture_output=True,
722
+ )
723
+ # Set permissions on public key (readable by all)
724
+ os.chmod(public_key_path, 0o644)
725
+
726
+ logger.info("Generated RSA keys for JWT signing")
727
+ except FileNotFoundError:
728
+ logger.warning("openssl not found, skipping JWT key generation (will be generated in container)")
729
+ except subprocess.CalledProcessError as e:
730
+ logger.error(f"Failed to generate RSA keys: {e}")
731
+
732
+
686
733
  def setup_host_directories():
687
734
  """Initialize OpenRAG directory structure on the host.
688
735
 
@@ -703,11 +750,14 @@ def setup_host_directories():
703
750
  base_dir / "data",
704
751
  base_dir / "data" / "opensearch-data",
705
752
  ]
706
-
753
+
707
754
  for directory in directories:
708
755
  directory.mkdir(parents=True, exist_ok=True)
709
756
  logger.debug(f"Ensured directory exists: {directory}")
710
757
 
758
+ # Generate JWT keys on host to avoid container permission issues
759
+ generate_jwt_keys(base_dir / "keys")
760
+
711
761
 
712
762
  def run_tui():
713
763
  """Run the OpenRAG TUI application."""
@@ -37,9 +37,9 @@ class DoclingManager:
37
37
  self._starting = False
38
38
  self._external_process = False
39
39
 
40
- # PID file to track docling-serve across sessions (in current working directory)
41
- from pathlib import Path
42
- self._pid_file = Path.cwd() / ".docling.pid"
40
+ # PID file to track docling-serve across sessions (centralized in ~/.openrag/tui/)
41
+ from utils.paths import get_tui_dir
42
+ self._pid_file = get_tui_dir() / ".docling.pid"
43
43
 
44
44
  # Log storage - simplified, no queue
45
45
  self._log_buffer: List[str] = []