OpenRAG 0.3.3__tar.gz → 0.4.0__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.3.3/src/openrag.egg-info → openrag-0.4.0}/PKG-INFO +2 -1
- {openrag-0.3.3 → openrag-0.4.0}/pyproject.toml +3 -2
- {openrag-0.3.3 → openrag-0.4.0}/src/agent.py +29 -24
- {openrag-0.3.3 → openrag-0.4.0}/src/api/connectors.py +57 -5
- {openrag-0.3.3 → openrag-0.4.0}/src/api/documents.py +15 -8
- {openrag-0.3.3 → openrag-0.4.0}/src/api/settings.py +150 -61
- {openrag-0.3.3 → openrag-0.4.0}/src/api/v1/chat.py +2 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/config/settings.py +2 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/connectors/__init__.py +4 -0
- openrag-0.4.0/src/connectors/aws_s3/__init__.py +19 -0
- openrag-0.4.0/src/connectors/aws_s3/api.py +175 -0
- openrag-0.4.0/src/connectors/aws_s3/auth.py +90 -0
- openrag-0.4.0/src/connectors/aws_s3/connector.py +277 -0
- openrag-0.4.0/src/connectors/aws_s3/models.py +13 -0
- openrag-0.4.0/src/connectors/aws_s3/support.py +51 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/connectors/connection_manager.py +57 -9
- openrag-0.4.0/src/connectors/ibm_cos/__init__.py +17 -0
- openrag-0.4.0/src/connectors/ibm_cos/api.py +202 -0
- openrag-0.4.0/src/connectors/ibm_cos/auth.py +183 -0
- openrag-0.4.0/src/connectors/ibm_cos/connector.py +375 -0
- openrag-0.4.0/src/connectors/ibm_cos/models.py +20 -0
- openrag-0.4.0/src/connectors/ibm_cos/support.py +68 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/connectors/langflow_connector_service.py +30 -29
- {openrag-0.3.3 → openrag-0.4.0}/src/main.py +75 -56
- {openrag-0.3.3 → openrag-0.4.0}/src/models/processors.py +52 -23
- {openrag-0.3.3 → openrag-0.4.0/src/openrag.egg-info}/PKG-INFO +2 -1
- {openrag-0.3.3 → openrag-0.4.0}/src/openrag.egg-info/SOURCES.txt +12 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/openrag.egg-info/requires.txt +1 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/services/auth_service.py +69 -4
- {openrag-0.3.3 → openrag-0.4.0}/src/services/chat_service.py +10 -9
- {openrag-0.3.3 → openrag-0.4.0}/src/services/flows_service.py +110 -36
- {openrag-0.3.3 → openrag-0.4.0}/src/services/search_service.py +70 -7
- {openrag-0.3.3 → openrag-0.4.0}/src/services/task_service.py +33 -24
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/_assets/docker-compose.yml +11 -1
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/_assets/flows/ingestion_flow.json +2 -2
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/_assets/flows/openrag_agent.json +4 -4
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/_assets/flows/openrag_nudges.json +2 -2
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/_assets/flows/openrag_url_mcp.json +2 -2
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/config_fields.py +47 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/main.py +83 -30
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/managers/container_manager.py +1 -1
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/managers/env_manager.py +31 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/screens/config.py +83 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/screens/monitor.py +25 -6
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/utils/version_check.py +10 -7
- {openrag-0.3.3 → openrag-0.4.0}/src/utils/file_utils.py +47 -5
- {openrag-0.3.3 → openrag-0.4.0}/src/utils/version_utils.py +6 -1
- {openrag-0.3.3 → openrag-0.4.0}/LICENSE +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/MANIFEST.in +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/README.md +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/setup.cfg +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/api/__init__.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/api/auth.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/api/chat.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/api/connector_router.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/api/docling.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/api/flows.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/api/keys.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/api/knowledge_filter.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/api/langflow_files.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/api/models.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/api/nudges.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/api/oidc.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/api/provider_health.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/api/provider_validation.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/api/router.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/api/search.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/api/tasks.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/api/upload.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/api/v1/__init__.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/api/v1/documents.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/api/v1/knowledge_filters.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/api/v1/models.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/api/v1/search.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/api/v1/settings.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/auth_context.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/config/__init__.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/config/config_manager.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/config/model_constants.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/connectors/base.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/connectors/google_drive/__init__.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/connectors/google_drive/connector.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/connectors/google_drive/oauth.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/connectors/onedrive/__init__.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/connectors/onedrive/connector.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/connectors/onedrive/oauth.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/connectors/service.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/connectors/sharepoint/__init__.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/connectors/sharepoint/connector.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/connectors/sharepoint/oauth.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/connectors/sharepoint/utils.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/dependencies.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/models/__init__.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/models/tasks.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/models/url.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/openrag.egg-info/dependency_links.txt +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/openrag.egg-info/entry_points.txt +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/openrag.egg-info/top_level.txt +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/services/__init__.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/services/api_key_service.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/services/conversation_persistence_service.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/services/document_service.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/services/knowledge_filter_service.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/services/langflow_file_service.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/services/langflow_history_service.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/services/langflow_mcp_service.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/services/models_service.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/services/monitor_service.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/services/session_ownership_service.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/session_manager.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/__init__.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/_assets/docker-compose.gpu.yml +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/_assets/flows/components/ollama_embedding.json +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/_assets/flows/components/ollama_llm.json +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/_assets/flows/components/ollama_llm_text.json +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/_assets/flows/components/watsonx_embedding.json +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/_assets/flows/components/watsonx_llm.json +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/_assets/flows/components/watsonx_llm_text.json +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/_assets/openrag-documents/docling.pdf +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/_assets/openrag-documents/ibm_anthropic.pdf +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/_assets/openrag-documents/openrag-documentation.pdf +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/_assets/openrag-documents/warmup_ocr.pdf +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/cli.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/managers/__init__.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/managers/docling_manager.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/screens/__init__.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/screens/diagnostics.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/screens/logs.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/screens/welcome.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/utils/__init__.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/utils/clipboard.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/utils/platform.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/utils/startup_checks.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/utils/validation.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/widgets/__init__.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/widgets/command_modal.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/widgets/diagnostics_notification.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/widgets/error_notification.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/widgets/factory_reset_warning_modal.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/widgets/flow_backup_warning_modal.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/widgets/prune_options_modal.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/widgets/upgrade_instructions_modal.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/widgets/version_mismatch_warning_modal.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/tui/widgets/waves.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/utils/__init__.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/utils/acl_utils.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/utils/container_utils.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/utils/docling_client.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/utils/document_processing.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/utils/embedding_fields.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/utils/embeddings.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/utils/env_utils.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/utils/gpu_detection.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/utils/hash_utils.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/utils/langflow_headers.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/utils/langflow_utils.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/utils/logging_config.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/utils/opensearch_queries.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/utils/opensearch_utils.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/utils/paths.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/utils/telemetry/__init__.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/utils/telemetry/category.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/src/utils/telemetry/client.py +0 -0
- {openrag-0.3.3 → openrag-0.4.0}/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.
|
|
3
|
+
Version: 0.4.0
|
|
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
|
|
@@ -28,6 +28,7 @@ Requires-Dist: python-multipart>=0.0.20
|
|
|
28
28
|
Requires-Dist: fastapi>=0.115.0
|
|
29
29
|
Requires-Dist: uvicorn>=0.35.0
|
|
30
30
|
Requires-Dist: boto3>=1.35.0
|
|
31
|
+
Requires-Dist: ibm-cos-sdk>=2.13.0
|
|
31
32
|
Requires-Dist: psutil>=7.0.0
|
|
32
33
|
Requires-Dist: rich>=13.0.0
|
|
33
34
|
Requires-Dist: textual>=0.45.0
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "openrag"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
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"
|
|
@@ -34,6 +34,7 @@ dependencies = [
|
|
|
34
34
|
"fastapi>=0.115.0",
|
|
35
35
|
"uvicorn>=0.35.0",
|
|
36
36
|
"boto3>=1.35.0",
|
|
37
|
+
"ibm-cos-sdk>=2.13.0",
|
|
37
38
|
"psutil>=7.0.0",
|
|
38
39
|
"rich>=13.0.0",
|
|
39
40
|
"textual>=0.45.0",
|
|
@@ -43,7 +44,7 @@ dependencies = [
|
|
|
43
44
|
"zxcvbn>=4.5.0",
|
|
44
45
|
"openai>=1.0.0",
|
|
45
46
|
"pyyaml>=6.0",
|
|
46
|
-
"tiktoken>=0.7.0"
|
|
47
|
+
"tiktoken>=0.7.0",
|
|
47
48
|
]
|
|
48
49
|
|
|
49
50
|
[dependency-groups]
|
|
@@ -815,31 +815,36 @@ async def async_langflow_chat_stream(
|
|
|
815
815
|
|
|
816
816
|
|
|
817
817
|
async def delete_user_conversation(user_id: str, response_id: str) -> bool:
|
|
818
|
-
"""Delete a conversation for a user from both memory and persistent storage
|
|
818
|
+
"""Delete a conversation for a user from both memory and persistent storage.
|
|
819
|
+
|
|
820
|
+
Returns:
|
|
821
|
+
True — conversation was found and deleted from at least one store.
|
|
822
|
+
False — conversation did not exist in any store (confirmed not-found).
|
|
823
|
+
|
|
824
|
+
Raises:
|
|
825
|
+
Exception — on unexpected storage errors so callers can distinguish
|
|
826
|
+
a confirmed "not found" from a backend failure.
|
|
827
|
+
"""
|
|
819
828
|
deleted = False
|
|
820
829
|
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
deleted = True
|
|
833
|
-
|
|
834
|
-
# Release session ownership
|
|
835
|
-
try:
|
|
836
|
-
from services.session_ownership_service import session_ownership_service
|
|
837
|
-
session_ownership_service.release_session(user_id, response_id)
|
|
838
|
-
logger.debug(f"Released session ownership for {response_id} for user {user_id}")
|
|
839
|
-
except Exception as e:
|
|
840
|
-
logger.warning(f"Failed to release session ownership: {e}")
|
|
830
|
+
# Delete from in-memory storage (cannot raise)
|
|
831
|
+
if user_id in active_conversations and response_id in active_conversations[user_id]:
|
|
832
|
+
del active_conversations[user_id][response_id]
|
|
833
|
+
logger.debug(f"Deleted conversation {response_id} from memory for user {user_id}")
|
|
834
|
+
deleted = True
|
|
835
|
+
|
|
836
|
+
# Delete from persistent storage — let real errors propagate to the caller
|
|
837
|
+
conversation_deleted = await conversation_persistence.delete_conversation_thread(user_id, response_id)
|
|
838
|
+
if conversation_deleted:
|
|
839
|
+
logger.debug(f"Deleted conversation {response_id} from persistent storage for user {user_id}")
|
|
840
|
+
deleted = True
|
|
841
841
|
|
|
842
|
-
|
|
842
|
+
# Release session ownership (best-effort; never masks storage errors above)
|
|
843
|
+
try:
|
|
844
|
+
from services.session_ownership_service import session_ownership_service
|
|
845
|
+
session_ownership_service.release_session(user_id, response_id)
|
|
846
|
+
logger.debug(f"Released session ownership for {response_id} for user {user_id}")
|
|
843
847
|
except Exception as e:
|
|
844
|
-
logger.
|
|
845
|
-
|
|
848
|
+
logger.warning(f"Failed to release session ownership: {e}")
|
|
849
|
+
|
|
850
|
+
return deleted
|
|
@@ -88,6 +88,11 @@ async def get_synced_file_ids_for_connector(
|
|
|
88
88
|
class ConnectorSyncBody(BaseModel):
|
|
89
89
|
max_files: Optional[int] = None
|
|
90
90
|
selected_files: Optional[List[Any]] = None
|
|
91
|
+
# When True, ingest ALL files from the connector (bypasses the existing-files gate).
|
|
92
|
+
# Used by direct-sync providers like IBM COS on initial ingest.
|
|
93
|
+
sync_all: bool = False
|
|
94
|
+
# When set, only ingest files from these buckets (IBM COS specific).
|
|
95
|
+
bucket_filter: Optional[List[str]] = None
|
|
91
96
|
|
|
92
97
|
|
|
93
98
|
async def list_connectors(
|
|
@@ -96,8 +101,8 @@ async def list_connectors(
|
|
|
96
101
|
):
|
|
97
102
|
"""List available connector types with metadata"""
|
|
98
103
|
try:
|
|
99
|
-
connector_types = (
|
|
100
|
-
|
|
104
|
+
connector_types = connector_service.connection_manager.get_available_connector_types(
|
|
105
|
+
user_id=user.user_id
|
|
101
106
|
)
|
|
102
107
|
return JSONResponse({"connectors": connector_types})
|
|
103
108
|
except Exception as e:
|
|
@@ -200,6 +205,51 @@ async def connector_sync(
|
|
|
200
205
|
jwt_token=jwt_token,
|
|
201
206
|
file_infos=file_infos,
|
|
202
207
|
)
|
|
208
|
+
elif body.sync_all or body.bucket_filter:
|
|
209
|
+
# Full ingest: discover and ingest all files (or files from specific buckets).
|
|
210
|
+
# Used by direct-sync providers (IBM COS) on initial ingest or per-bucket sync.
|
|
211
|
+
logger.info(
|
|
212
|
+
"Full connector ingest requested",
|
|
213
|
+
connector_type=connector_type,
|
|
214
|
+
bucket_filter=body.bucket_filter,
|
|
215
|
+
)
|
|
216
|
+
connector = await connector_service.get_connector(working_connection.connection_id)
|
|
217
|
+
if body.bucket_filter:
|
|
218
|
+
# List only files from the requested buckets, then sync_specific_files
|
|
219
|
+
original_buckets = connector.bucket_names
|
|
220
|
+
connector.bucket_names = body.bucket_filter
|
|
221
|
+
try:
|
|
222
|
+
all_file_ids = []
|
|
223
|
+
page_token = None
|
|
224
|
+
while True:
|
|
225
|
+
result = await connector.list_files(page_token=page_token)
|
|
226
|
+
for f in result.get("files", []):
|
|
227
|
+
all_file_ids.append(f["id"])
|
|
228
|
+
page_token = result.get("next_page_token")
|
|
229
|
+
if not page_token:
|
|
230
|
+
break
|
|
231
|
+
finally:
|
|
232
|
+
connector.bucket_names = original_buckets
|
|
233
|
+
|
|
234
|
+
if not all_file_ids:
|
|
235
|
+
return JSONResponse(
|
|
236
|
+
{"status": "no_files", "message": "No files found in the selected buckets."},
|
|
237
|
+
status_code=200,
|
|
238
|
+
)
|
|
239
|
+
task_id = await connector_service.sync_specific_files(
|
|
240
|
+
working_connection.connection_id,
|
|
241
|
+
user.user_id,
|
|
242
|
+
all_file_ids,
|
|
243
|
+
jwt_token=jwt_token,
|
|
244
|
+
)
|
|
245
|
+
else:
|
|
246
|
+
# sync_all: ingest everything the connector can see
|
|
247
|
+
task_id = await connector_service.sync_connector_files(
|
|
248
|
+
working_connection.connection_id,
|
|
249
|
+
user.user_id,
|
|
250
|
+
max_files=max_files,
|
|
251
|
+
jwt_token=jwt_token,
|
|
252
|
+
)
|
|
203
253
|
else:
|
|
204
254
|
# No files specified - sync only files already in OpenSearch for this connector
|
|
205
255
|
# This ensures deleted files stay deleted
|
|
@@ -209,7 +259,7 @@ async def connector_sync(
|
|
|
209
259
|
session_manager=session_manager,
|
|
210
260
|
jwt_token=jwt_token,
|
|
211
261
|
)
|
|
212
|
-
|
|
262
|
+
|
|
213
263
|
if not existing_file_ids and not existing_filenames:
|
|
214
264
|
return JSONResponse(
|
|
215
265
|
{
|
|
@@ -218,7 +268,7 @@ async def connector_sync(
|
|
|
218
268
|
},
|
|
219
269
|
status_code=200,
|
|
220
270
|
)
|
|
221
|
-
|
|
271
|
+
|
|
222
272
|
# If we have document_ids (connector file IDs), use sync_specific_files
|
|
223
273
|
# Otherwise, use filename filtering with sync_connector_files
|
|
224
274
|
if existing_file_ids:
|
|
@@ -602,6 +652,8 @@ async def connector_disconnect(
|
|
|
602
652
|
)
|
|
603
653
|
|
|
604
654
|
|
|
655
|
+
# ---------------------------------------------------------------------------
|
|
656
|
+
|
|
605
657
|
async def sync_all_connectors(
|
|
606
658
|
connector_service=Depends(get_connector_service),
|
|
607
659
|
session_manager=Depends(get_session_manager),
|
|
@@ -615,7 +667,7 @@ async def sync_all_connectors(
|
|
|
615
667
|
jwt_token = user.jwt_token
|
|
616
668
|
|
|
617
669
|
# Cloud connector types to sync
|
|
618
|
-
cloud_connector_types = ["google_drive", "onedrive", "sharepoint"]
|
|
670
|
+
cloud_connector_types = ["google_drive", "onedrive", "sharepoint", "ibm_cos", "aws_s3"]
|
|
619
671
|
|
|
620
672
|
all_task_ids = []
|
|
621
673
|
synced_connectors = []
|
|
@@ -121,16 +121,26 @@ async def check_filename_exists(
|
|
|
121
121
|
)
|
|
122
122
|
|
|
123
123
|
from utils.opensearch_queries import build_filename_search_body
|
|
124
|
+
from utils.file_utils import get_filename_aliases
|
|
124
125
|
|
|
125
|
-
|
|
126
|
+
candidate_filenames = get_filename_aliases(filename)
|
|
127
|
+
if not candidate_filenames:
|
|
128
|
+
return JSONResponse({"exists": False, "filename": filename}, status_code=200)
|
|
126
129
|
|
|
127
130
|
logger.debug("Checking filename existence", filename=filename, index_name=get_index_name())
|
|
131
|
+
exists = False
|
|
128
132
|
|
|
129
133
|
try:
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
+
for candidate in candidate_filenames:
|
|
135
|
+
search_body = build_filename_search_body(candidate, size=1, source=["filename"])
|
|
136
|
+
response = await opensearch_client.search(
|
|
137
|
+
index=get_index_name(),
|
|
138
|
+
body=search_body
|
|
139
|
+
)
|
|
140
|
+
hits = response.get("hits", {}).get("hits", [])
|
|
141
|
+
if hits:
|
|
142
|
+
exists = True
|
|
143
|
+
break
|
|
134
144
|
except Exception as search_err:
|
|
135
145
|
if "index_not_found_exception" in str(search_err):
|
|
136
146
|
logger.info("Index does not exist, creating it now before upload")
|
|
@@ -138,9 +148,6 @@ async def check_filename_exists(
|
|
|
138
148
|
return JSONResponse({"exists": False, "filename": filename}, status_code=200)
|
|
139
149
|
raise
|
|
140
150
|
|
|
141
|
-
hits = response.get("hits", {}).get("hits", [])
|
|
142
|
-
exists = len(hits) > 0
|
|
143
|
-
|
|
144
151
|
return JSONResponse({"exists": exists, "filename": filename}, status_code=200)
|
|
145
152
|
|
|
146
153
|
except Exception as e:
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
import json
|
|
2
3
|
import platform
|
|
3
4
|
from fastapi import Depends, Request, HTTPException
|
|
@@ -36,6 +37,7 @@ from dependencies import (
|
|
|
36
37
|
from session_manager import User
|
|
37
38
|
|
|
38
39
|
logger = get_logger(__name__)
|
|
40
|
+
_background_tasks: set[asyncio.Task] = set()
|
|
39
41
|
|
|
40
42
|
|
|
41
43
|
class SettingsUpdateBody(BaseModel):
|
|
@@ -209,6 +211,9 @@ class RollbackResponse(BaseModel):
|
|
|
209
211
|
cancelled_tasks: int
|
|
210
212
|
deleted_files: int
|
|
211
213
|
|
|
214
|
+
class RollbackBody(BaseModel):
|
|
215
|
+
embedding_only: bool = False
|
|
216
|
+
|
|
212
217
|
|
|
213
218
|
# Docling preset configurations
|
|
214
219
|
def get_docling_preset_configs(
|
|
@@ -605,6 +610,12 @@ async def update_settings(
|
|
|
605
610
|
logger.error(f"Failed to update docling settings in flow: {str(e)}")
|
|
606
611
|
|
|
607
612
|
if body.chunk_size is not None:
|
|
613
|
+
effective_overlap = body.chunk_overlap if body.chunk_overlap is not None else current_config.knowledge.chunk_overlap
|
|
614
|
+
if effective_overlap >= body.chunk_size:
|
|
615
|
+
raise HTTPException(
|
|
616
|
+
status_code=422,
|
|
617
|
+
detail="chunk_overlap must be less than chunk_size"
|
|
618
|
+
)
|
|
608
619
|
current_config.knowledge.chunk_size = body.chunk_size
|
|
609
620
|
config_updated = True
|
|
610
621
|
await TelemetryClient.send_event(
|
|
@@ -625,6 +636,12 @@ async def update_settings(
|
|
|
625
636
|
# The config will still be saved
|
|
626
637
|
|
|
627
638
|
if body.chunk_overlap is not None:
|
|
639
|
+
effective_chunk_size = body.chunk_size if body.chunk_size is not None else current_config.knowledge.chunk_size
|
|
640
|
+
if body.chunk_overlap >= effective_chunk_size:
|
|
641
|
+
raise HTTPException(
|
|
642
|
+
status_code=422,
|
|
643
|
+
detail="chunk_overlap must be less than chunk_size"
|
|
644
|
+
)
|
|
628
645
|
current_config.knowledge.chunk_overlap = body.chunk_overlap
|
|
629
646
|
config_updated = True
|
|
630
647
|
await TelemetryClient.send_event(
|
|
@@ -815,30 +832,31 @@ async def update_settings(
|
|
|
815
832
|
{"error": "Failed to save configuration"}, status_code=500
|
|
816
833
|
)
|
|
817
834
|
|
|
818
|
-
#
|
|
835
|
+
# Refresh patched client immediately so subsequent requests pick up latest config.
|
|
819
836
|
await clients.refresh_patched_client()
|
|
820
837
|
|
|
838
|
+
# Run expensive Langflow sync in the background to keep settings updates responsive.
|
|
821
839
|
if should_validate or provider_updated:
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
840
|
+
task = asyncio.create_task(
|
|
841
|
+
_run_async_post_save_langflow_updates(
|
|
842
|
+
session_manager=session_manager,
|
|
843
|
+
update_mcp_servers=(
|
|
844
|
+
body.embedding_provider is not None
|
|
845
|
+
or body.embedding_model is not None
|
|
846
|
+
or provider_updated
|
|
847
|
+
),
|
|
848
|
+
update_model_values=(
|
|
849
|
+
body.llm_provider is not None
|
|
850
|
+
or body.llm_model is not None
|
|
851
|
+
or body.embedding_provider is not None
|
|
852
|
+
or body.embedding_model is not None
|
|
853
|
+
or provider_updated
|
|
854
|
+
),
|
|
855
|
+
)
|
|
856
|
+
)
|
|
857
|
+
# Keep a strong reference until completion to avoid premature GC cancellation.
|
|
858
|
+
_background_tasks.add(task)
|
|
859
|
+
task.add_done_callback(_background_tasks.discard)
|
|
842
860
|
|
|
843
861
|
|
|
844
862
|
set_fields = [k for k, v in body.model_dump().items() if v is not None]
|
|
@@ -1380,6 +1398,37 @@ async def _update_langflow_global_variables(config, flows_service=None):
|
|
|
1380
1398
|
raise
|
|
1381
1399
|
|
|
1382
1400
|
|
|
1401
|
+
async def _run_async_post_save_langflow_updates(
|
|
1402
|
+
session_manager,
|
|
1403
|
+
update_mcp_servers: bool,
|
|
1404
|
+
update_model_values: bool,
|
|
1405
|
+
) -> None:
|
|
1406
|
+
"""Apply post-save Langflow synchronization asynchronously."""
|
|
1407
|
+
try:
|
|
1408
|
+
current_config = get_openrag_config()
|
|
1409
|
+
flows_service = _get_flows_service()
|
|
1410
|
+
|
|
1411
|
+
# Update global variables
|
|
1412
|
+
await _update_langflow_global_variables(
|
|
1413
|
+
current_config, flows_service=flows_service
|
|
1414
|
+
)
|
|
1415
|
+
|
|
1416
|
+
# Update LLM client credentials when embedding selection changes
|
|
1417
|
+
if update_mcp_servers:
|
|
1418
|
+
await _update_mcp_servers_with_provider_credentials(
|
|
1419
|
+
current_config, session_manager, flows_service=flows_service
|
|
1420
|
+
)
|
|
1421
|
+
|
|
1422
|
+
# Update model values if provider/model changed (including removals/fallbacks)
|
|
1423
|
+
if update_model_values:
|
|
1424
|
+
await _update_langflow_model_values(current_config, flows_service)
|
|
1425
|
+
|
|
1426
|
+
logger.info("Completed asynchronous Langflow post-save sync")
|
|
1427
|
+
except Exception as e:
|
|
1428
|
+
# Do not fail user request if async sync fails; keep parity with existing behavior.
|
|
1429
|
+
logger.error(f"Failed to update Langflow settings asynchronously: {str(e)}")
|
|
1430
|
+
|
|
1431
|
+
|
|
1383
1432
|
async def _update_mcp_servers_with_provider_credentials(config, session_manager = None, flows_service=None):
|
|
1384
1433
|
# Update MCP servers with provider credentials
|
|
1385
1434
|
try:
|
|
@@ -1592,8 +1641,10 @@ async def reapply_all_settings(session_manager = None):
|
|
|
1592
1641
|
|
|
1593
1642
|
async def rollback_onboarding(
|
|
1594
1643
|
request: Request,
|
|
1644
|
+
body: Optional[RollbackBody] = None,
|
|
1595
1645
|
session_manager=Depends(get_session_manager),
|
|
1596
1646
|
task_service=Depends(get_task_service),
|
|
1647
|
+
knowledge_filter_service=Depends(get_knowledge_filter_service),
|
|
1597
1648
|
user: User = Depends(get_current_user),
|
|
1598
1649
|
) -> RollbackResponse:
|
|
1599
1650
|
"""Rollback onboarding configuration when sample data files fail.
|
|
@@ -1623,7 +1674,36 @@ async def rollback_onboarding(
|
|
|
1623
1674
|
cancelled_tasks = []
|
|
1624
1675
|
deleted_files = []
|
|
1625
1676
|
|
|
1677
|
+
# Delete knowledge filters created during onboarding
|
|
1678
|
+
try:
|
|
1679
|
+
async def remove_filter(filter_id: Optional[str]):
|
|
1680
|
+
if filter_id and knowledge_filter_service:
|
|
1681
|
+
try:
|
|
1682
|
+
result = await knowledge_filter_service.delete_knowledge_filter(
|
|
1683
|
+
filter_id, user.user_id, user.jwt_token
|
|
1684
|
+
)
|
|
1685
|
+
if result and result.get("success"):
|
|
1686
|
+
logger.info(f"Deleted knowledge filter {filter_id}")
|
|
1687
|
+
else:
|
|
1688
|
+
error_msg = result.get("error") if result else "Unknown error"
|
|
1689
|
+
logger.warning(f"Could not delete knowledge filter {filter_id}: {error_msg}")
|
|
1690
|
+
except Exception as e:
|
|
1691
|
+
logger.warning(f"Exception deleting knowledge filter {filter_id}: {str(e)}")
|
|
1692
|
+
|
|
1693
|
+
if getattr(current_config.onboarding, 'openrag_docs_filter_id', None):
|
|
1694
|
+
await remove_filter(current_config.onboarding.openrag_docs_filter_id)
|
|
1695
|
+
current_config.onboarding.openrag_docs_filter_id = None
|
|
1696
|
+
|
|
1697
|
+
if getattr(current_config.onboarding, 'user_doc_filter_id', None):
|
|
1698
|
+
await remove_filter(current_config.onboarding.user_doc_filter_id)
|
|
1699
|
+
current_config.onboarding.user_doc_filter_id = None
|
|
1700
|
+
except Exception as e:
|
|
1701
|
+
logger.error(f"Error while cleaning up knowledge filters: {e}")
|
|
1702
|
+
|
|
1626
1703
|
# Cancel all active tasks and collect successfully ingested files
|
|
1704
|
+
from session_manager import AnonymousUser
|
|
1705
|
+
anonymous_user_id = AnonymousUser().user_id
|
|
1706
|
+
|
|
1627
1707
|
for task_data in all_tasks:
|
|
1628
1708
|
task_id = task_data.get("task_id")
|
|
1629
1709
|
task_status = task_data.get("status")
|
|
@@ -1638,41 +1718,40 @@ async def rollback_onboarding(
|
|
|
1638
1718
|
except Exception as e:
|
|
1639
1719
|
logger.error(f"Failed to cancel task {task_id}: {str(e)}")
|
|
1640
1720
|
|
|
1641
|
-
#
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
logger.error(f"Failed to delete documents for {filename}: {str(e)}")
|
|
1721
|
+
# Delete all files associated with any task, regardless of whether
|
|
1722
|
+
# the task failed or completed, to ensure no partial chunks remain in OpenSearch.
|
|
1723
|
+
files = task_data.get("files", {})
|
|
1724
|
+
if isinstance(files, dict):
|
|
1725
|
+
for file_path, file_info in files.items():
|
|
1726
|
+
if isinstance(file_info, dict):
|
|
1727
|
+
filename = file_info.get("filename") or file_path.split("/")[-1]
|
|
1728
|
+
if filename:
|
|
1729
|
+
try:
|
|
1730
|
+
opensearch_client = session_manager.get_user_opensearch_client(
|
|
1731
|
+
user.user_id, jwt_token
|
|
1732
|
+
)
|
|
1733
|
+
from utils.opensearch_queries import build_filename_delete_body
|
|
1734
|
+
from config.settings import get_index_name
|
|
1735
|
+
|
|
1736
|
+
delete_query = build_filename_delete_body(filename)
|
|
1737
|
+
result = await opensearch_client.delete_by_query(
|
|
1738
|
+
index=get_index_name(),
|
|
1739
|
+
body=delete_query,
|
|
1740
|
+
conflicts="proceed"
|
|
1741
|
+
)
|
|
1742
|
+
deleted_count = result.get("deleted", 0)
|
|
1743
|
+
if deleted_count > 0:
|
|
1744
|
+
deleted_files.append(filename)
|
|
1745
|
+
logger.info(f"Deleted {deleted_count} chunks for filename {filename}")
|
|
1746
|
+
except Exception as e:
|
|
1747
|
+
logger.error(f"Failed to delete documents for {filename}: {str(e)}")
|
|
1748
|
+
|
|
1749
|
+
# Wipe the task completely from memory so the frontend doesn't see it anymore
|
|
1750
|
+
for check_user_id in [user.user_id, anonymous_user_id]:
|
|
1751
|
+
if check_user_id in task_service.task_store and task_id in task_service.task_store[check_user_id]:
|
|
1752
|
+
task_service._task_locks.pop(task_id, None)
|
|
1753
|
+
task_service.task_store[check_user_id].pop(task_id, None)
|
|
1754
|
+
logger.info(f"Purged task {task_id} completely from task_store for user {check_user_id}")
|
|
1676
1755
|
|
|
1677
1756
|
# Clear embedding provider and model settings
|
|
1678
1757
|
current_config.knowledge.embedding_provider = "openai" # Reset to default
|
|
@@ -1680,11 +1759,21 @@ async def rollback_onboarding(
|
|
|
1680
1759
|
current_config.onboarding.openrag_docs_ingested_version = None
|
|
1681
1760
|
current_config.onboarding.openrag_docs_remote_signature = None
|
|
1682
1761
|
|
|
1762
|
+
embedding_only = body.embedding_only if body else False
|
|
1763
|
+
|
|
1683
1764
|
# Mark config as not edited so user can go through onboarding again
|
|
1684
|
-
|
|
1685
|
-
|
|
1765
|
+
if not embedding_only:
|
|
1766
|
+
current_config.edited = False
|
|
1767
|
+
current_config.onboarding.current_step = 0
|
|
1768
|
+
# Also clear LLM provider and model settings when doing a full rollback
|
|
1769
|
+
current_config.agent.llm_provider = "openai" # Reset to default
|
|
1770
|
+
current_config.agent.llm_model = ""
|
|
1771
|
+
else:
|
|
1772
|
+
# When rolling back embedding only, we keep edited=True
|
|
1773
|
+
# and set current_step to 1 (which is the embedding step)
|
|
1774
|
+
current_config.onboarding.current_step = 1
|
|
1686
1775
|
|
|
1687
|
-
# Save the rolled back configuration manually
|
|
1776
|
+
# Save the rolled back configuration manually
|
|
1688
1777
|
try:
|
|
1689
1778
|
import yaml
|
|
1690
1779
|
config_file = config_manager.config_file
|
|
@@ -1692,14 +1781,14 @@ async def rollback_onboarding(
|
|
|
1692
1781
|
# Ensure directory exists
|
|
1693
1782
|
config_file.parent.mkdir(parents=True, exist_ok=True)
|
|
1694
1783
|
|
|
1695
|
-
# Save config with edited
|
|
1784
|
+
# Save config with current edited state
|
|
1696
1785
|
with open(config_file, "w") as f:
|
|
1697
1786
|
yaml.dump(current_config.to_dict(), f, default_flow_style=False, indent=2)
|
|
1698
1787
|
|
|
1699
1788
|
# Update cached config
|
|
1700
1789
|
config_manager._config = current_config
|
|
1701
1790
|
|
|
1702
|
-
logger.info("Successfully saved rolled back configuration with edited=
|
|
1791
|
+
logger.info(f"Successfully saved rolled back configuration with edited={current_config.edited}")
|
|
1703
1792
|
except Exception as e:
|
|
1704
1793
|
logger.error(f"Failed to save rolled back configuration: {e}")
|
|
1705
1794
|
return JSONResponse(
|
|
@@ -226,6 +226,8 @@ async def chat_delete_endpoint(
|
|
|
226
226
|
"""Delete a conversation. DELETE /v1/chat/{chat_id}"""
|
|
227
227
|
try:
|
|
228
228
|
result = await chat_service.delete_session(user.user_id, chat_id)
|
|
229
|
+
if result.get("not_found"):
|
|
230
|
+
return JSONResponse({"error": "Conversation not found"}, status_code=404)
|
|
229
231
|
if result.get("success"):
|
|
230
232
|
return JSONResponse({"success": True})
|
|
231
233
|
else:
|
|
@@ -50,6 +50,8 @@ GOOGLE_OAUTH_CLIENT_ID = os.getenv("GOOGLE_OAUTH_CLIENT_ID")
|
|
|
50
50
|
GOOGLE_OAUTH_CLIENT_SECRET = os.getenv("GOOGLE_OAUTH_CLIENT_SECRET")
|
|
51
51
|
DOCLING_OCR_ENGINE = os.getenv("DOCLING_OCR_ENGINE")
|
|
52
52
|
|
|
53
|
+
IBM_AUTH_ENABLED = os.getenv("IBM_AUTH_ENABLED", "false").lower() in ("true", "1", "yes")
|
|
54
|
+
|
|
53
55
|
# Ingestion configuration
|
|
54
56
|
DISABLE_INGEST_WITH_LANGFLOW = os.getenv(
|
|
55
57
|
"DISABLE_INGEST_WITH_LANGFLOW", "false"
|
|
@@ -2,10 +2,14 @@ from .base import BaseConnector
|
|
|
2
2
|
from .google_drive import GoogleDriveConnector
|
|
3
3
|
from .sharepoint import SharePointConnector
|
|
4
4
|
from .onedrive import OneDriveConnector
|
|
5
|
+
from .ibm_cos import IBMCOSConnector
|
|
6
|
+
from .aws_s3 import S3Connector
|
|
5
7
|
|
|
6
8
|
__all__ = [
|
|
7
9
|
"BaseConnector",
|
|
8
10
|
"GoogleDriveConnector",
|
|
9
11
|
"SharePointConnector",
|
|
10
12
|
"OneDriveConnector",
|
|
13
|
+
"IBMCOSConnector",
|
|
14
|
+
"S3Connector",
|
|
11
15
|
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Amazon S3 / S3-compatible connector for OpenRAG."""
|
|
2
|
+
|
|
3
|
+
from .connector import S3Connector
|
|
4
|
+
from .models import S3ConfigureBody
|
|
5
|
+
from .api import (
|
|
6
|
+
s3_defaults,
|
|
7
|
+
s3_configure,
|
|
8
|
+
s3_list_buckets,
|
|
9
|
+
s3_bucket_status,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"S3Connector",
|
|
14
|
+
"S3ConfigureBody",
|
|
15
|
+
"s3_defaults",
|
|
16
|
+
"s3_configure",
|
|
17
|
+
"s3_list_buckets",
|
|
18
|
+
"s3_bucket_status",
|
|
19
|
+
]
|