OpenRAG 0.2.5__tar.gz → 0.3.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.
- {openrag-0.2.5/src/openrag.egg-info → openrag-0.3.1}/PKG-INFO +19 -7
- {openrag-0.2.5 → openrag-0.3.1}/README.md +8 -6
- {openrag-0.2.5 → openrag-0.3.1}/pyproject.toml +13 -1
- {openrag-0.2.5 → openrag-0.3.1}/src/agent.py +98 -46
- {openrag-0.2.5 → openrag-0.3.1}/src/api/documents.py +94 -39
- {openrag-0.2.5 → openrag-0.3.1}/src/api/settings.py +240 -39
- {openrag-0.2.5 → openrag-0.3.1}/src/api/v1/chat.py +22 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/v1/documents.py +8 -28
- {openrag-0.2.5 → openrag-0.3.1}/src/config/config_manager.py +4 -1
- {openrag-0.2.5 → openrag-0.3.1}/src/config/settings.py +15 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/dependencies.py +7 -3
- openrag-0.3.1/src/main.py +1933 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/models/processors.py +21 -20
- openrag-0.3.1/src/models/url.py +157 -0
- {openrag-0.2.5 → openrag-0.3.1/src/openrag.egg-info}/PKG-INFO +19 -7
- {openrag-0.2.5 → openrag-0.3.1}/src/openrag.egg-info/SOURCES.txt +1 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/services/auth_service.py +3 -2
- {openrag-0.2.5 → openrag-0.3.1}/src/services/chat_service.py +15 -5
- {openrag-0.2.5 → openrag-0.3.1}/src/services/flows_service.py +342 -597
- {openrag-0.2.5 → openrag-0.3.1}/src/services/langflow_file_service.py +262 -26
- {openrag-0.2.5 → openrag-0.3.1}/src/services/langflow_history_service.py +10 -2
- {openrag-0.2.5 → openrag-0.3.1}/src/services/models_service.py +0 -1
- {openrag-0.2.5 → openrag-0.3.1}/src/services/search_service.py +5 -5
- {openrag-0.2.5 → openrag-0.3.1}/src/services/task_service.py +32 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/session_manager.py +12 -16
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/_assets/docker-compose.yml +12 -11
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/_assets/flows/ingestion_flow.json +2378 -1810
- openrag-0.3.1/src/tui/_assets/flows/openrag_agent.json +5230 -0
- openrag-0.3.1/src/tui/_assets/flows/openrag_nudges.json +4851 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/_assets/flows/openrag_url_mcp.json +1766 -1454
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/main.py +15 -2
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/managers/container_manager.py +100 -137
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/managers/env_manager.py +58 -14
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/utils/startup_checks.py +86 -4
- {openrag-0.2.5 → openrag-0.3.1}/src/utils/container_utils.py +18 -23
- {openrag-0.2.5 → openrag-0.3.1}/src/utils/embedding_fields.py +15 -2
- {openrag-0.2.5 → openrag-0.3.1}/src/utils/langflow_headers.py +14 -6
- {openrag-0.2.5 → openrag-0.3.1}/src/utils/opensearch_queries.py +1 -1
- {openrag-0.2.5 → openrag-0.3.1}/src/utils/telemetry/message_id.py +14 -0
- openrag-0.2.5/src/main.py +0 -995
- openrag-0.2.5/src/tui/_assets/flows/openrag_agent.json +0 -4795
- openrag-0.2.5/src/tui/_assets/flows/openrag_nudges.json +0 -4122
- {openrag-0.2.5 → openrag-0.3.1}/LICENSE +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/MANIFEST.in +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/setup.cfg +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/auth.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/chat.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/connector_router.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/connectors.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/docling.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/flows.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/keys.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/knowledge_filter.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/langflow_files.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/models.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/nudges.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/oidc.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/provider_health.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/provider_validation.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/router.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/search.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/tasks.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/upload.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/v1/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/v1/knowledge_filters.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/v1/models.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/v1/search.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/api/v1/settings.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/auth_context.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/config/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/config/model_constants.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/connectors/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/connectors/base.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/connectors/connection_manager.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/connectors/google_drive/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/connectors/google_drive/connector.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/connectors/google_drive/oauth.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/connectors/langflow_connector_service.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/connectors/onedrive/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/connectors/onedrive/connector.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/connectors/onedrive/oauth.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/connectors/service.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/connectors/sharepoint/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/connectors/sharepoint/connector.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/connectors/sharepoint/oauth.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/connectors/sharepoint/utils.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/models/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/models/tasks.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/openrag.egg-info/dependency_links.txt +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/openrag.egg-info/entry_points.txt +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/openrag.egg-info/requires.txt +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/openrag.egg-info/top_level.txt +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/services/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/services/api_key_service.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/services/conversation_persistence_service.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/services/document_service.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/services/knowledge_filter_service.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/services/langflow_mcp_service.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/services/monitor_service.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/services/session_ownership_service.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/_assets/docker-compose.gpu.yml +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/_assets/flows/components/ollama_embedding.json +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/_assets/flows/components/ollama_llm.json +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/_assets/flows/components/ollama_llm_text.json +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/_assets/flows/components/watsonx_embedding.json +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/_assets/flows/components/watsonx_llm.json +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/_assets/flows/components/watsonx_llm_text.json +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/_assets/openrag-documents/docling.pdf +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/_assets/openrag-documents/ibm_anthropic.pdf +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/_assets/openrag-documents/openrag-documentation.pdf +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/_assets/openrag-documents/warmup_ocr.pdf +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/cli.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/config_fields.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/managers/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/managers/docling_manager.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/screens/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/screens/config.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/screens/diagnostics.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/screens/logs.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/screens/monitor.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/screens/welcome.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/utils/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/utils/clipboard.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/utils/platform.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/utils/validation.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/utils/version_check.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/widgets/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/widgets/command_modal.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/widgets/diagnostics_notification.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/widgets/error_notification.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/widgets/factory_reset_warning_modal.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/widgets/flow_backup_warning_modal.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/widgets/prune_options_modal.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/widgets/upgrade_instructions_modal.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/widgets/version_mismatch_warning_modal.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/tui/widgets/waves.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/utils/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/utils/acl_utils.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/utils/docling_client.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/utils/document_processing.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/utils/embeddings.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/utils/env_utils.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/utils/file_utils.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/utils/gpu_detection.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/utils/hash_utils.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/utils/langflow_utils.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/utils/logging_config.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/utils/opensearch_utils.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/utils/paths.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/utils/telemetry/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/utils/telemetry/category.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/utils/telemetry/client.py +0 -0
- {openrag-0.2.5 → openrag-0.3.1}/src/utils/version_utils.py +0 -0
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openrag
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: OpenRAG is a comprehensive Retrieval-Augmented Generation platform that enables intelligent document search and AI-powered conversations.
|
|
5
|
+
Classifier: Development Status :: 4 - Beta
|
|
6
|
+
Classifier: Environment :: Console
|
|
7
|
+
Classifier: Intended Audience :: Developers
|
|
8
|
+
Classifier: Intended Audience :: Science/Research
|
|
9
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
5
15
|
Requires-Python: >=3.13
|
|
6
16
|
Description-Content-Type: text/markdown
|
|
7
17
|
License-File: LICENSE
|
|
@@ -30,7 +40,7 @@ Dynamic: license-file
|
|
|
30
40
|
|
|
31
41
|
<div align="center">
|
|
32
42
|
|
|
33
|
-
<img src="./docs/static/img/openrag-logo-dog.svg" alt="
|
|
43
|
+
<img src="./docs/static/img/openrag-logo-dog.svg" alt="" width="120"/>
|
|
34
44
|
|
|
35
45
|
# OpenRAG
|
|
36
46
|
|
|
@@ -39,7 +49,7 @@ Dynamic: license-file
|
|
|
39
49
|
</h3>
|
|
40
50
|
|
|
41
51
|
<!-- Badges -->
|
|
42
|
-
|
|
52
|
+
|
|
43
53
|
[](https://github.com/langflow-ai/langflow)
|
|
44
54
|
[](https://github.com/opensearch-project/OpenSearch)
|
|
45
55
|
[](https://github.com/docling-project/docling)
|
|
@@ -48,7 +58,7 @@ Dynamic: license-file
|
|
|
48
58
|
[](https://github.com/langflow-ai/openrag/stargazers)
|
|
49
59
|
[](https://github.com/langflow-ai/openrag/network/members)
|
|
50
60
|
|
|
51
|
-
[](https://deepwiki.com/langflow-ai/openrag)
|
|
61
|
+
[](https://docs.openr.ag) [](https://deepwiki.com/langflow-ai/openrag)
|
|
52
62
|
|
|
53
63
|
</div>
|
|
54
64
|
|
|
@@ -56,9 +66,11 @@ Dynamic: license-file
|
|
|
56
66
|
|
|
57
67
|
OpenRAG is a comprehensive Retrieval-Augmented Generation platform that enables intelligent document search and AI-powered conversations.
|
|
58
68
|
|
|
59
|
-
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
|
+
|
|
71
|
+
Check out the [documentation](https://docs.openr.ag/) or get started with the [quickstart](https://docs.openr.ag/quickstart).
|
|
60
72
|
|
|
61
|
-
Built with [
|
|
73
|
+
Built with [FastAPI](https://fastapi.tiangolo.com/) and [Next.js](https://github.com/vercel/next.js).
|
|
62
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).
|
|
63
75
|
|
|
64
76
|
---
|
|
@@ -189,7 +201,7 @@ The MCP server provides tools for RAG-enhanced chat, semantic search, and settin
|
|
|
189
201
|
|
|
190
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).
|
|
191
203
|
|
|
192
|
-
##
|
|
204
|
+
## 🛟 Troubleshooting
|
|
193
205
|
|
|
194
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).
|
|
195
207
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
<img src="./docs/static/img/openrag-logo-dog.svg" alt="
|
|
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
|
[](https://github.com/langflow-ai/langflow)
|
|
14
14
|
[](https://github.com/opensearch-project/OpenSearch)
|
|
15
15
|
[](https://github.com/docling-project/docling)
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
[](https://github.com/langflow-ai/openrag/stargazers)
|
|
19
19
|
[](https://github.com/langflow-ai/openrag/network/members)
|
|
20
20
|
|
|
21
|
-
[](https://deepwiki.com/langflow-ai/openrag)
|
|
21
|
+
[](https://docs.openr.ag) [](https://deepwiki.com/langflow-ai/openrag)
|
|
22
22
|
|
|
23
23
|
</div>
|
|
24
24
|
|
|
@@ -26,9 +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
|
+
|
|
31
|
+
Check out the [documentation](https://docs.openr.ag/) or get started with the [quickstart](https://docs.openr.ag/quickstart).
|
|
30
32
|
|
|
31
|
-
Built with [
|
|
33
|
+
Built with [FastAPI](https://fastapi.tiangolo.com/) and [Next.js](https://github.com/vercel/next.js).
|
|
32
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).
|
|
33
35
|
|
|
34
36
|
---
|
|
@@ -159,7 +161,7 @@ The MCP server provides tools for RAG-enhanced chat, semantic search, and settin
|
|
|
159
161
|
|
|
160
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).
|
|
161
163
|
|
|
162
|
-
##
|
|
164
|
+
## 🛟 Troubleshooting
|
|
163
165
|
|
|
164
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).
|
|
165
167
|
|
|
@@ -4,10 +4,22 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "openrag"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.1"
|
|
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"
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 4 - Beta",
|
|
13
|
+
"Environment :: Console",
|
|
14
|
+
"Intended Audience :: Developers",
|
|
15
|
+
"Intended Audience :: Science/Research",
|
|
16
|
+
"License :: OSI Approved :: Apache Software License",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
21
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
22
|
+
]
|
|
11
23
|
dependencies = [
|
|
12
24
|
"agentd>=0.2.2",
|
|
13
25
|
"aiofiles>=24.1.0",
|
|
@@ -135,14 +135,13 @@ async def async_response_stream(
|
|
|
135
135
|
|
|
136
136
|
full_response = ""
|
|
137
137
|
chunk_count = 0
|
|
138
|
-
detected_tool_call = False
|
|
138
|
+
detected_tool_call = False
|
|
139
139
|
async for chunk in response:
|
|
140
140
|
chunk_count += 1
|
|
141
141
|
logger.debug(
|
|
142
142
|
"Stream chunk received", chunk_count=chunk_count, chunk=str(chunk)
|
|
143
143
|
)
|
|
144
144
|
|
|
145
|
-
# Yield the raw event as JSON for the UI to process
|
|
146
145
|
import json
|
|
147
146
|
|
|
148
147
|
# Also extract text content for logging
|
|
@@ -186,7 +185,7 @@ async def async_response_stream(
|
|
|
186
185
|
if isinstance(chunk_data, dict):
|
|
187
186
|
# Check for any fields that might indicate tool usage
|
|
188
187
|
potential_tool_fields = {
|
|
189
|
-
k: v for k, v in chunk_data.items()
|
|
188
|
+
k: v for k, v in chunk_data.items()
|
|
190
189
|
if any(keyword in str(k).lower() for keyword in ['tool', 'call', 'retrieval', 'function', 'result', 'output'])
|
|
191
190
|
}
|
|
192
191
|
if potential_tool_fields:
|
|
@@ -235,8 +234,8 @@ async def async_response_stream(
|
|
|
235
234
|
"tool_name": "Retrieval",
|
|
236
235
|
"status": "completed",
|
|
237
236
|
"inputs": {"implicit": True, "backend_detected": True},
|
|
238
|
-
"results": chunk_data.get('results') or chunk_data.get('outputs') or
|
|
239
|
-
chunk_data.get('retrieved_documents') or
|
|
237
|
+
"results": chunk_data.get('results') or chunk_data.get('outputs') or
|
|
238
|
+
chunk_data.get('retrieved_documents') or
|
|
240
239
|
chunk_data.get('retrieval_results') or []
|
|
241
240
|
}
|
|
242
241
|
}
|
|
@@ -625,8 +624,25 @@ async def async_langflow_chat(
|
|
|
625
624
|
message_count=len(conversation_state["messages"]),
|
|
626
625
|
)
|
|
627
626
|
|
|
627
|
+
# Extract sources from retrieval tool calls in the response
|
|
628
|
+
sources = []
|
|
629
|
+
if hasattr(response_obj, "output") and response_obj.output:
|
|
630
|
+
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
|
+
})
|
|
643
|
+
|
|
628
644
|
if not store_conversation:
|
|
629
|
-
return response_text, response_id
|
|
645
|
+
return response_text, response_id, sources
|
|
630
646
|
|
|
631
647
|
# Store the conversation thread with its response_id
|
|
632
648
|
if response_id:
|
|
@@ -662,7 +678,7 @@ async def async_langflow_chat(
|
|
|
662
678
|
else:
|
|
663
679
|
logger.warning("No response_id received from langflow, conversation not stored")
|
|
664
680
|
|
|
665
|
-
return response_text, response_id
|
|
681
|
+
return response_text, response_id, sources
|
|
666
682
|
|
|
667
683
|
|
|
668
684
|
# Async langflow function with conversation storage (streaming)
|
|
@@ -698,47 +714,55 @@ async def async_langflow_chat_stream(
|
|
|
698
714
|
response_id = None
|
|
699
715
|
usage_data = None
|
|
700
716
|
collected_chunks = [] # Store all chunks for function call data
|
|
717
|
+
error_occurred = False
|
|
701
718
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
response_id
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
719
|
+
try:
|
|
720
|
+
async for chunk in async_stream(
|
|
721
|
+
langflow_client,
|
|
722
|
+
prompt,
|
|
723
|
+
flow_id,
|
|
724
|
+
extra_headers=extra_headers,
|
|
725
|
+
previous_response_id=previous_response_id,
|
|
726
|
+
log_prefix="langflow",
|
|
727
|
+
):
|
|
728
|
+
# Extract text content to build full response for history
|
|
729
|
+
try:
|
|
730
|
+
import json
|
|
731
|
+
|
|
732
|
+
chunk_data = json.loads(chunk.decode("utf-8"))
|
|
733
|
+
collected_chunks.append(chunk_data) # Collect all chunk data
|
|
734
|
+
|
|
735
|
+
if "delta" in chunk_data and "content" in chunk_data["delta"]:
|
|
736
|
+
full_response += chunk_data["delta"]["content"]
|
|
737
|
+
# Extract response_id from chunk
|
|
738
|
+
if "id" in chunk_data:
|
|
739
|
+
response_id = chunk_data["id"]
|
|
740
|
+
elif "response_id" in chunk_data:
|
|
741
|
+
response_id = chunk_data["response_id"]
|
|
742
|
+
|
|
743
|
+
# Check for error status
|
|
744
|
+
if chunk_data.get("finish_reason") == "error" or chunk_data.get("status") == "failed":
|
|
745
|
+
error_occurred = True
|
|
746
|
+
logger.error("Error detected in Langflow stream chunk")
|
|
747
|
+
# Capture usage from response.completed event
|
|
748
|
+
if chunk_data.get("type") == "response.completed":
|
|
749
|
+
response_obj = chunk_data.get("response", {})
|
|
750
|
+
usage_data = response_obj.get("usage")
|
|
751
|
+
except:
|
|
752
|
+
pass
|
|
753
|
+
yield chunk
|
|
731
754
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
755
|
+
# Add the complete assistant response to message history with response_id, timestamp, and function call data
|
|
756
|
+
if full_response:
|
|
757
|
+
assistant_message = {
|
|
758
|
+
"role": "assistant",
|
|
759
|
+
"content": full_response,
|
|
760
|
+
"response_id": response_id,
|
|
761
|
+
"timestamp": datetime.now(),
|
|
762
|
+
"chunks": collected_chunks, # Store complete chunk data for function calls
|
|
763
|
+
"error": error_occurred, # Mark if this was an error response
|
|
764
|
+
}
|
|
765
|
+
# Store usage data if available (from response.completed event)
|
|
742
766
|
if usage_data:
|
|
743
767
|
assistant_message["response_data"] = {"usage": usage_data}
|
|
744
768
|
conversation_state["messages"].append(assistant_message)
|
|
@@ -760,6 +784,34 @@ async def async_langflow_chat_stream(
|
|
|
760
784
|
logger.debug(
|
|
761
785
|
f"Stored langflow conversation thread for user {user_id} with response_id: {response_id}"
|
|
762
786
|
)
|
|
787
|
+
except Exception as e:
|
|
788
|
+
# Log the error
|
|
789
|
+
logger.error(f"Error in langflow chat stream: {e}", exc_info=True)
|
|
790
|
+
error_occurred = True
|
|
791
|
+
|
|
792
|
+
# Store error message in conversation history so it persists
|
|
793
|
+
error_message = {
|
|
794
|
+
"role": "assistant",
|
|
795
|
+
"content": f"Sorry, I encountered an error: {str(e)}",
|
|
796
|
+
"timestamp": datetime.now(),
|
|
797
|
+
"error": True,
|
|
798
|
+
}
|
|
799
|
+
conversation_state["messages"].append(error_message)
|
|
800
|
+
|
|
801
|
+
# Try to store the conversation with error message
|
|
802
|
+
# Use a temporary response_id if we don't have one
|
|
803
|
+
if not response_id:
|
|
804
|
+
response_id = f"error_{user_id}_{int(datetime.now().timestamp())}"
|
|
805
|
+
|
|
806
|
+
try:
|
|
807
|
+
conversation_state["last_activity"] = datetime.now()
|
|
808
|
+
await store_conversation_thread(user_id, response_id, conversation_state)
|
|
809
|
+
logger.debug(f"Stored conversation with error for user {user_id}")
|
|
810
|
+
except Exception as store_error:
|
|
811
|
+
logger.error(f"Failed to store error conversation: {store_error}")
|
|
812
|
+
|
|
813
|
+
# Re-raise the exception so it propagates to the API layer
|
|
814
|
+
raise
|
|
763
815
|
|
|
764
816
|
|
|
765
817
|
async def delete_user_conversation(user_id: str, response_id: str) -> bool:
|
|
@@ -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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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)
|