openrag 0.4.0.dev5__tar.gz → 0.4.0.dev7__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.4.0.dev5/src/openrag.egg-info → openrag-0.4.0.dev7}/PKG-INFO +1 -1
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/pyproject.toml +1 -1
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/settings.py +12 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/main.py +17 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7/src/openrag.egg-info}/PKG-INFO +1 -1
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/openrag.egg-info/SOURCES.txt +1 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/services/flows_service.py +110 -36
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/_assets/docker-compose.yml +3 -1
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/_assets/flows/ingestion_flow.json +2 -2
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/_assets/flows/openrag_agent.json +2 -2
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/_assets/flows/openrag_nudges.json +2 -2
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/_assets/flows/openrag_url_mcp.json +2 -2
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/config_fields.py +6 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/managers/container_manager.py +1 -1
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/managers/env_manager.py +5 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/screens/config.py +83 -0
- openrag-0.4.0.dev7/src/utils/opensearch_filter_merge.py +188 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/LICENSE +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/MANIFEST.in +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/README.md +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/setup.cfg +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/agent.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/__init__.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/auth.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/chat.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/connector_router.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/connectors.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/docling.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/documents.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/flows.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/keys.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/knowledge_filter.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/langflow_files.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/models.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/nudges.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/oidc.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/provider_health.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/provider_validation.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/router.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/search.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/tasks.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/upload.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/v1/__init__.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/v1/chat.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/v1/documents.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/v1/knowledge_filters.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/v1/models.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/v1/search.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/api/v1/settings.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/auth_context.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/config/__init__.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/config/config_manager.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/config/model_constants.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/config/settings.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/__init__.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/aws_s3/__init__.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/aws_s3/api.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/aws_s3/auth.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/aws_s3/connector.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/aws_s3/models.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/aws_s3/support.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/base.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/connection_manager.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/google_drive/__init__.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/google_drive/connector.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/google_drive/oauth.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/ibm_cos/__init__.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/ibm_cos/api.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/ibm_cos/auth.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/ibm_cos/connector.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/ibm_cos/models.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/ibm_cos/support.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/langflow_connector_service.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/onedrive/__init__.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/onedrive/connector.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/onedrive/oauth.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/service.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/sharepoint/__init__.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/sharepoint/connector.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/sharepoint/oauth.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/connectors/sharepoint/utils.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/dependencies.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/models/__init__.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/models/processors.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/models/tasks.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/models/url.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/openrag.egg-info/dependency_links.txt +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/openrag.egg-info/entry_points.txt +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/openrag.egg-info/requires.txt +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/openrag.egg-info/top_level.txt +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/services/__init__.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/services/api_key_service.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/services/auth_service.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/services/chat_service.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/services/conversation_persistence_service.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/services/document_service.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/services/knowledge_filter_service.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/services/langflow_file_service.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/services/langflow_history_service.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/services/langflow_mcp_service.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/services/models_service.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/services/monitor_service.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/services/search_service.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/services/session_ownership_service.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/services/task_service.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/session_manager.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/__init__.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/_assets/docker-compose.gpu.yml +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/_assets/flows/components/ollama_embedding.json +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/_assets/flows/components/ollama_llm.json +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/_assets/flows/components/ollama_llm_text.json +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/_assets/flows/components/watsonx_embedding.json +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/_assets/flows/components/watsonx_llm.json +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/_assets/flows/components/watsonx_llm_text.json +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/_assets/openrag-documents/docling.pdf +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/_assets/openrag-documents/ibm_anthropic.pdf +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/_assets/openrag-documents/openrag-documentation.pdf +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/_assets/openrag-documents/warmup_ocr.pdf +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/cli.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/main.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/managers/__init__.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/managers/docling_manager.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/screens/__init__.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/screens/diagnostics.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/screens/logs.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/screens/monitor.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/screens/welcome.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/utils/__init__.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/utils/clipboard.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/utils/platform.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/utils/startup_checks.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/utils/validation.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/utils/version_check.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/widgets/__init__.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/widgets/command_modal.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/widgets/diagnostics_notification.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/widgets/error_notification.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/widgets/factory_reset_warning_modal.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/widgets/flow_backup_warning_modal.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/widgets/prune_options_modal.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/widgets/upgrade_instructions_modal.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/widgets/version_mismatch_warning_modal.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/tui/widgets/waves.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/utils/__init__.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/utils/acl_utils.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/utils/container_utils.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/utils/docling_client.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/utils/document_processing.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/utils/embedding_fields.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/utils/embeddings.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/utils/env_utils.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/utils/file_utils.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/utils/gpu_detection.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/utils/hash_utils.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/utils/langflow_headers.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/utils/langflow_utils.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/utils/logging_config.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/utils/opensearch_queries.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/utils/opensearch_utils.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/utils/paths.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/utils/telemetry/__init__.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/utils/telemetry/category.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/utils/telemetry/client.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/utils/telemetry/message_id.py +0 -0
- {openrag-0.4.0.dev5 → openrag-0.4.0.dev7}/src/utils/version_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openrag
|
|
3
|
-
Version: 0.4.0.
|
|
3
|
+
Version: 0.4.0.dev7
|
|
4
4
|
Summary: OpenRAG is a comprehensive Retrieval-Augmented Generation platform that enables intelligent document search and AI-powered conversations.
|
|
5
5
|
Classifier: Development Status :: 4 - Beta
|
|
6
6
|
Classifier: Environment :: Console
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "openrag"
|
|
7
|
-
version = "0.4.0.
|
|
7
|
+
version = "0.4.0.dev7"
|
|
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"
|
|
@@ -607,6 +607,12 @@ async def update_settings(
|
|
|
607
607
|
logger.error(f"Failed to update docling settings in flow: {str(e)}")
|
|
608
608
|
|
|
609
609
|
if body.chunk_size is not None:
|
|
610
|
+
effective_overlap = body.chunk_overlap if body.chunk_overlap is not None else current_config.knowledge.chunk_overlap
|
|
611
|
+
if effective_overlap >= body.chunk_size:
|
|
612
|
+
raise HTTPException(
|
|
613
|
+
status_code=422,
|
|
614
|
+
detail="chunk_overlap must be less than chunk_size"
|
|
615
|
+
)
|
|
610
616
|
current_config.knowledge.chunk_size = body.chunk_size
|
|
611
617
|
config_updated = True
|
|
612
618
|
await TelemetryClient.send_event(
|
|
@@ -627,6 +633,12 @@ async def update_settings(
|
|
|
627
633
|
# The config will still be saved
|
|
628
634
|
|
|
629
635
|
if body.chunk_overlap is not None:
|
|
636
|
+
effective_chunk_size = body.chunk_size if body.chunk_size is not None else current_config.knowledge.chunk_size
|
|
637
|
+
if body.chunk_overlap >= effective_chunk_size:
|
|
638
|
+
raise HTTPException(
|
|
639
|
+
status_code=422,
|
|
640
|
+
detail="chunk_overlap must be less than chunk_size"
|
|
641
|
+
)
|
|
630
642
|
current_config.knowledge.chunk_overlap = body.chunk_overlap
|
|
631
643
|
config_updated = True
|
|
632
644
|
await TelemetryClient.send_event(
|
|
@@ -1198,6 +1198,20 @@ async def startup_tasks(services):
|
|
|
1198
1198
|
# Update MCP servers with provider credentials (especially important for no-auth mode)
|
|
1199
1199
|
await _update_mcp_servers_with_provider_credentials(services)
|
|
1200
1200
|
|
|
1201
|
+
# Ensure all configured flows exist in Langflow (create-only, never overwrites).
|
|
1202
|
+
# This replaces LANGFLOW_LOAD_FLOWS_PATH, which performed a blind upsert on
|
|
1203
|
+
# every container start and discarded any user edits made in the Langflow UI.
|
|
1204
|
+
newly_created: set[str] = set()
|
|
1205
|
+
try:
|
|
1206
|
+
flows_service = services["flows_service"]
|
|
1207
|
+
newly_created = await flows_service.ensure_flows_exist()
|
|
1208
|
+
except Exception as e:
|
|
1209
|
+
logger.error(
|
|
1210
|
+
"Failed to ensure Langflow flows exist at startup — "
|
|
1211
|
+
"flows may be missing until the next restart",
|
|
1212
|
+
error=str(e),
|
|
1213
|
+
)
|
|
1214
|
+
|
|
1201
1215
|
# Check if flows were reset and reapply settings if config is edited
|
|
1202
1216
|
try:
|
|
1203
1217
|
config = get_openrag_config()
|
|
@@ -1205,6 +1219,9 @@ async def startup_tasks(services):
|
|
|
1205
1219
|
logger.info("Checking if Langflow flows were reset")
|
|
1206
1220
|
flows_service = services["flows_service"]
|
|
1207
1221
|
reset_flows = await flows_service.check_flows_reset()
|
|
1222
|
+
# Exclude flows that were just seeded — they match the JSON by design,
|
|
1223
|
+
# not because they were externally reset.
|
|
1224
|
+
reset_flows = [f for f in reset_flows if f not in newly_created]
|
|
1208
1225
|
|
|
1209
1226
|
if reset_flows:
|
|
1210
1227
|
logger.info(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openrag
|
|
3
|
-
Version: 0.4.0.
|
|
3
|
+
Version: 0.4.0.dev7
|
|
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
|
|
@@ -152,6 +152,7 @@ src/utils/hash_utils.py
|
|
|
152
152
|
src/utils/langflow_headers.py
|
|
153
153
|
src/utils/langflow_utils.py
|
|
154
154
|
src/utils/logging_config.py
|
|
155
|
+
src/utils/opensearch_filter_merge.py
|
|
155
156
|
src/utils/opensearch_queries.py
|
|
156
157
|
src/utils/opensearch_utils.py
|
|
157
158
|
src/utils/paths.py
|
|
@@ -47,7 +47,7 @@ class FlowsService:
|
|
|
47
47
|
resolved_url = None
|
|
48
48
|
for cand in candidates:
|
|
49
49
|
test_url = replace_localhost_patterns(endpoint, cand)
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
logger.debug(f"Probing Ollama candidate via Langflow: {test_url}")
|
|
52
52
|
try:
|
|
53
53
|
response = await clients.langflow_request(
|
|
@@ -61,7 +61,7 @@ class FlowsService:
|
|
|
61
61
|
except Exception as e:
|
|
62
62
|
logger.debug(f"Probe failed for {test_url}: {e}")
|
|
63
63
|
continue
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
if not resolved_url:
|
|
66
66
|
# Fallback to simple transformation if probing fails
|
|
67
67
|
resolved_url = transform_localhost_url(endpoint)
|
|
@@ -95,23 +95,23 @@ class FlowsService:
|
|
|
95
95
|
def _get_latest_backup_path(self, flow_id: str, flow_type: str):
|
|
96
96
|
"""
|
|
97
97
|
Get the path to the latest backup file for a flow.
|
|
98
|
-
|
|
98
|
+
|
|
99
99
|
Args:
|
|
100
100
|
flow_id: The flow ID
|
|
101
101
|
flow_type: The flow type name
|
|
102
|
-
|
|
102
|
+
|
|
103
103
|
Returns:
|
|
104
104
|
str: Path to latest backup file, or None if no backup exists
|
|
105
105
|
"""
|
|
106
106
|
backup_dir = self._get_backup_directory()
|
|
107
|
-
|
|
107
|
+
|
|
108
108
|
if not os.path.exists(backup_dir):
|
|
109
109
|
return None
|
|
110
|
-
|
|
110
|
+
|
|
111
111
|
# Find all backup files for this flow
|
|
112
112
|
backup_files = []
|
|
113
113
|
prefix = f"{flow_type}_"
|
|
114
|
-
|
|
114
|
+
|
|
115
115
|
try:
|
|
116
116
|
for filename in os.listdir(backup_dir):
|
|
117
117
|
if filename.startswith(prefix) and filename.endswith(".json"):
|
|
@@ -122,10 +122,10 @@ class FlowsService:
|
|
|
122
122
|
except Exception as e:
|
|
123
123
|
logger.warning(f"Error reading backup directory: {str(e)}")
|
|
124
124
|
return None
|
|
125
|
-
|
|
125
|
+
|
|
126
126
|
if not backup_files:
|
|
127
127
|
return None
|
|
128
|
-
|
|
128
|
+
|
|
129
129
|
# Return the most recent backup (highest mtime)
|
|
130
130
|
backup_files.sort(key=lambda x: x[0], reverse=True)
|
|
131
131
|
return backup_files[0][1]
|
|
@@ -134,17 +134,17 @@ class FlowsService:
|
|
|
134
134
|
"""
|
|
135
135
|
Compare two flow structures to see if they're different.
|
|
136
136
|
Normalizes both flows before comparison.
|
|
137
|
-
|
|
137
|
+
|
|
138
138
|
Args:
|
|
139
139
|
flow1: First flow data
|
|
140
140
|
flow2: Second flow data
|
|
141
|
-
|
|
141
|
+
|
|
142
142
|
Returns:
|
|
143
143
|
bool: True if flows are different, False if they're the same
|
|
144
144
|
"""
|
|
145
145
|
normalized1 = self._normalize_flow_structure(flow1)
|
|
146
146
|
normalized2 = self._normalize_flow_structure(flow2)
|
|
147
|
-
|
|
147
|
+
|
|
148
148
|
# Compare normalized structures
|
|
149
149
|
return normalized1 != normalized2
|
|
150
150
|
|
|
@@ -152,10 +152,10 @@ class FlowsService:
|
|
|
152
152
|
"""
|
|
153
153
|
Backup all flows from Langflow to the backup folder.
|
|
154
154
|
Only backs up flows that have changed since the last backup.
|
|
155
|
-
|
|
155
|
+
|
|
156
156
|
Args:
|
|
157
157
|
only_if_changed: If True, only backup flows that differ from latest backup
|
|
158
|
-
|
|
158
|
+
|
|
159
159
|
Returns:
|
|
160
160
|
dict: Summary of backup operations with success/failure status
|
|
161
161
|
"""
|
|
@@ -200,7 +200,7 @@ class FlowsService:
|
|
|
200
200
|
flow_locked = current_flow.get("locked", False)
|
|
201
201
|
latest_backup_path = self._get_latest_backup_path(flow_id, flow_type)
|
|
202
202
|
has_backups = latest_backup_path is not None
|
|
203
|
-
|
|
203
|
+
|
|
204
204
|
# If flow is locked and no backups exist, skip backup
|
|
205
205
|
if flow_locked and not has_backups:
|
|
206
206
|
logger.debug(
|
|
@@ -212,13 +212,13 @@ class FlowsService:
|
|
|
212
212
|
"reason": "locked_without_backups",
|
|
213
213
|
})
|
|
214
214
|
continue
|
|
215
|
-
|
|
215
|
+
|
|
216
216
|
# Check if we need to backup (only if changed)
|
|
217
217
|
if only_if_changed and has_backups:
|
|
218
218
|
try:
|
|
219
219
|
with open(latest_backup_path, "r") as f:
|
|
220
220
|
latest_backup = json.load(f)
|
|
221
|
-
|
|
221
|
+
|
|
222
222
|
# Compare flows
|
|
223
223
|
if not self._compare_flows(current_flow, latest_backup):
|
|
224
224
|
logger.debug(
|
|
@@ -280,12 +280,12 @@ class FlowsService:
|
|
|
280
280
|
async def _backup_flow(self, flow_id: str, flow_type: str, flow_data: dict = None):
|
|
281
281
|
"""
|
|
282
282
|
Backup a single flow to the backup folder.
|
|
283
|
-
|
|
283
|
+
|
|
284
284
|
Args:
|
|
285
285
|
flow_id: The flow ID to backup
|
|
286
286
|
flow_type: The flow type name (nudges, retrieval, ingest, url_ingest)
|
|
287
287
|
flow_data: The flow data to backup (if None, fetches from API)
|
|
288
|
-
|
|
288
|
+
|
|
289
289
|
Returns:
|
|
290
290
|
str: Path to the backup file, or None if backup failed
|
|
291
291
|
"""
|
|
@@ -717,7 +717,7 @@ class FlowsService:
|
|
|
717
717
|
for node in nodes:
|
|
718
718
|
node_data = node.get("data", {})
|
|
719
719
|
node_template = node_data.get("node", {})
|
|
720
|
-
|
|
720
|
+
|
|
721
721
|
normalized_node = {
|
|
722
722
|
"id": node.get("id"), # Keep ID for edge matching
|
|
723
723
|
"type": node.get("type"),
|
|
@@ -775,20 +775,20 @@ class FlowsService:
|
|
|
775
775
|
# Compare entire normalized structures exactly
|
|
776
776
|
# Sort nodes and edges for consistent comparison
|
|
777
777
|
normalized_langflow["data"]["nodes"] = sorted(
|
|
778
|
-
normalized_langflow["data"]["nodes"],
|
|
778
|
+
normalized_langflow["data"]["nodes"],
|
|
779
779
|
key=lambda x: (x.get("id", ""), x.get("type", ""))
|
|
780
780
|
)
|
|
781
781
|
normalized_file["data"]["nodes"] = sorted(
|
|
782
|
-
normalized_file["data"]["nodes"],
|
|
782
|
+
normalized_file["data"]["nodes"],
|
|
783
783
|
key=lambda x: (x.get("id", ""), x.get("type", ""))
|
|
784
784
|
)
|
|
785
785
|
|
|
786
786
|
normalized_langflow["data"]["edges"] = sorted(
|
|
787
|
-
normalized_langflow["data"]["edges"],
|
|
787
|
+
normalized_langflow["data"]["edges"],
|
|
788
788
|
key=lambda x: (x.get("source", ""), x.get("target", ""), x.get("sourceHandle", ""), x.get("targetHandle", ""))
|
|
789
789
|
)
|
|
790
790
|
normalized_file["data"]["edges"] = sorted(
|
|
791
|
-
normalized_file["data"]["edges"],
|
|
791
|
+
normalized_file["data"]["edges"],
|
|
792
792
|
key=lambda x: (x.get("source", ""), x.get("target", ""), x.get("sourceHandle", ""), x.get("targetHandle", ""))
|
|
793
793
|
)
|
|
794
794
|
|
|
@@ -799,6 +799,80 @@ class FlowsService:
|
|
|
799
799
|
logger.error(f"Error comparing flow {flow_id} with file: {str(e)}")
|
|
800
800
|
return False
|
|
801
801
|
|
|
802
|
+
async def ensure_flows_exist(self) -> set[str]:
|
|
803
|
+
"""
|
|
804
|
+
Ensure all configured flows exist in Langflow.
|
|
805
|
+
|
|
806
|
+
Creates flows from their JSON files if they are not already present in
|
|
807
|
+
the Langflow database. This is intentionally create-only: it never
|
|
808
|
+
patches or overwrites an existing flow, preserving any edits the user
|
|
809
|
+
has made in the Langflow UI.
|
|
810
|
+
|
|
811
|
+
This replaces the LANGFLOW_LOAD_FLOWS_PATH mechanism, which performed a
|
|
812
|
+
blind upsert on every container start and discarded user edits.
|
|
813
|
+
|
|
814
|
+
Returns the set of flow type names that were actually created.
|
|
815
|
+
"""
|
|
816
|
+
flow_configs = [
|
|
817
|
+
("nudges", NUDGES_FLOW_ID),
|
|
818
|
+
("retrieval", LANGFLOW_CHAT_FLOW_ID),
|
|
819
|
+
("ingest", LANGFLOW_INGEST_FLOW_ID),
|
|
820
|
+
("url_ingest", LANGFLOW_URL_INGEST_FLOW_ID),
|
|
821
|
+
]
|
|
822
|
+
created_flow_types: set[str] = set()
|
|
823
|
+
|
|
824
|
+
for flow_type, flow_id in flow_configs:
|
|
825
|
+
if not flow_id:
|
|
826
|
+
continue
|
|
827
|
+
|
|
828
|
+
try:
|
|
829
|
+
response = await clients.langflow_request(
|
|
830
|
+
"GET", f"/api/v1/flows/{flow_id}"
|
|
831
|
+
)
|
|
832
|
+
if response.status_code == 200:
|
|
833
|
+
logger.info(
|
|
834
|
+
f"Flow {flow_type} (ID: {flow_id}) already exists, skipping creation"
|
|
835
|
+
)
|
|
836
|
+
continue
|
|
837
|
+
|
|
838
|
+
if response.status_code != 404:
|
|
839
|
+
logger.warning(
|
|
840
|
+
f"Unexpected status checking {flow_type} flow (ID: {flow_id}): "
|
|
841
|
+
f"HTTP {response.status_code} — skipping creation to avoid overwriting existing data"
|
|
842
|
+
)
|
|
843
|
+
continue
|
|
844
|
+
|
|
845
|
+
flow_path = self._find_flow_file_by_id(flow_id)
|
|
846
|
+
if not flow_path:
|
|
847
|
+
logger.warning(
|
|
848
|
+
f"No flow file found for {flow_type} (ID: {flow_id}), cannot create"
|
|
849
|
+
)
|
|
850
|
+
continue
|
|
851
|
+
|
|
852
|
+
with open(flow_path, "r") as f:
|
|
853
|
+
flow_data = json.load(f)
|
|
854
|
+
|
|
855
|
+
response = await clients.langflow_request(
|
|
856
|
+
"PUT", f"/api/v1/flows/{flow_id}", json=flow_data
|
|
857
|
+
)
|
|
858
|
+
if response.status_code in (200, 201):
|
|
859
|
+
logger.info(
|
|
860
|
+
f"Created {flow_type} flow (ID: {flow_id}) from {os.path.basename(flow_path)}"
|
|
861
|
+
)
|
|
862
|
+
created_flow_types.add(flow_type)
|
|
863
|
+
else:
|
|
864
|
+
logger.warning(
|
|
865
|
+
f"Failed to create {flow_type} flow (ID: {flow_id}): "
|
|
866
|
+
f"HTTP {response.status_code} — {response.text}"
|
|
867
|
+
)
|
|
868
|
+
|
|
869
|
+
except Exception as e:
|
|
870
|
+
logger.error(
|
|
871
|
+
f"Error ensuring {flow_type} flow (ID: {flow_id}) exists: {e}"
|
|
872
|
+
)
|
|
873
|
+
|
|
874
|
+
return created_flow_types
|
|
875
|
+
|
|
802
876
|
async def check_flows_reset(self):
|
|
803
877
|
"""
|
|
804
878
|
Check if any flows have been reset by comparing with JSON files.
|
|
@@ -819,7 +893,7 @@ class FlowsService:
|
|
|
819
893
|
|
|
820
894
|
logger.info(f"Checking if {flow_type} flow (ID: {flow_id}) was reset")
|
|
821
895
|
is_reset = await self._compare_flow_with_file(flow_id)
|
|
822
|
-
|
|
896
|
+
|
|
823
897
|
if is_reset:
|
|
824
898
|
logger.info(f"Flow {flow_type} (ID: {flow_id}) appears to have been reset")
|
|
825
899
|
reset_flows.append(flow_type)
|
|
@@ -827,7 +901,7 @@ class FlowsService:
|
|
|
827
901
|
logger.info(f"Flow {flow_type} (ID: {flow_id}) does not match reset state")
|
|
828
902
|
|
|
829
903
|
return reset_flows
|
|
830
|
-
|
|
904
|
+
|
|
831
905
|
async def change_langflow_model_value(
|
|
832
906
|
self,
|
|
833
907
|
provider: str,
|
|
@@ -917,23 +991,23 @@ class FlowsService:
|
|
|
917
991
|
# Get all embedding nodes in the flow
|
|
918
992
|
embedding_nodes = self._find_nodes_in_flow(flow_data, display_name=OPENAI_EMBEDDING_COMPONENT_DISPLAY_NAME)
|
|
919
993
|
logger.info(f"Found {len(embedding_nodes)} embedding nodes in flow {flow_name} with display name '{OPENAI_EMBEDDING_COMPONENT_DISPLAY_NAME}'")
|
|
920
|
-
|
|
994
|
+
|
|
921
995
|
# Count configured embedding-enabled providers
|
|
922
996
|
config_obj = get_openrag_config()
|
|
923
997
|
configured_providers = []
|
|
924
998
|
if config_obj.providers.openai.configured: configured_providers.append("openai")
|
|
925
999
|
if config_obj.providers.watsonx.configured: configured_providers.append("watsonx")
|
|
926
1000
|
if config_obj.providers.ollama.configured: configured_providers.append("ollama")
|
|
927
|
-
|
|
1001
|
+
|
|
928
1002
|
# Ensure current provider is in the list for counting purposes if it's being configured
|
|
929
1003
|
if provider in ["openai", "watsonx", "ollama"] and provider not in configured_providers:
|
|
930
1004
|
configured_providers.append(provider)
|
|
931
|
-
|
|
1005
|
+
|
|
932
1006
|
all_possible = ["openai", "watsonx", "ollama"]
|
|
933
1007
|
configured_providers = [p for p in all_possible if p in configured_providers]
|
|
934
1008
|
provider_count = len(configured_providers)
|
|
935
1009
|
logger.info(f"Configured embedding providers: {configured_providers} (count: {provider_count})")
|
|
936
|
-
|
|
1010
|
+
|
|
937
1011
|
# Determine slot mapping context
|
|
938
1012
|
if provider_count == 1:
|
|
939
1013
|
logger.info("Configuration mode: all 3 slots belong to the single active provider")
|
|
@@ -948,7 +1022,7 @@ class FlowsService:
|
|
|
948
1022
|
for node, idx in embedding_nodes:
|
|
949
1023
|
if self._get_node_provider(node) == provider_display:
|
|
950
1024
|
matched_nodes.append((node, idx))
|
|
951
|
-
|
|
1025
|
+
|
|
952
1026
|
if matched_nodes:
|
|
953
1027
|
logger.info(f"Found {len(matched_nodes)} nodes already configured for provider '{provider}'")
|
|
954
1028
|
for node, idx in matched_nodes:
|
|
@@ -1035,7 +1109,7 @@ class FlowsService:
|
|
|
1035
1109
|
# Only call if code field exists (custom components should have code)
|
|
1036
1110
|
if "code" in template and "value" in template["code"]:
|
|
1037
1111
|
code_value = template["code"]["value"]
|
|
1038
|
-
|
|
1112
|
+
|
|
1039
1113
|
try:
|
|
1040
1114
|
update_payload = {
|
|
1041
1115
|
"code": code_value,
|
|
@@ -1044,11 +1118,11 @@ class FlowsService:
|
|
|
1044
1118
|
"field_value": model,
|
|
1045
1119
|
"tool_mode": False,
|
|
1046
1120
|
}
|
|
1047
|
-
|
|
1121
|
+
|
|
1048
1122
|
response = await clients.langflow_request(
|
|
1049
1123
|
"POST", "/api/v1/custom_component/update", json=update_payload
|
|
1050
1124
|
)
|
|
1051
|
-
|
|
1125
|
+
|
|
1052
1126
|
if response.status_code == 200:
|
|
1053
1127
|
response_data = response.json()
|
|
1054
1128
|
# Update template with the new template from response.data
|
|
@@ -1161,11 +1235,11 @@ class FlowsService:
|
|
|
1161
1235
|
"model_id": model_value,
|
|
1162
1236
|
"enabled": True
|
|
1163
1237
|
}]
|
|
1164
|
-
|
|
1238
|
+
|
|
1165
1239
|
response = await clients.langflow_request(
|
|
1166
1240
|
"POST", "/api/v1/models/enabled_models", json=enable_payload
|
|
1167
1241
|
)
|
|
1168
|
-
|
|
1242
|
+
|
|
1169
1243
|
if response.status_code == 200:
|
|
1170
1244
|
logger.info(f"Successfully enabled model {model_value} for provider {provider_name}")
|
|
1171
1245
|
else:
|
|
@@ -127,6 +127,7 @@ services:
|
|
|
127
127
|
langflow:
|
|
128
128
|
volumes:
|
|
129
129
|
- ${OPENRAG_FLOWS_PATH:-./flows}:/app/flows:U,z
|
|
130
|
+
- ${LANGFLOW_DATA_PATH:-./langflow-data}:/app/langflow-data:U,z
|
|
130
131
|
image: langflowai/openrag-langflow:${OPENRAG_VERSION:-latest}
|
|
131
132
|
build:
|
|
132
133
|
context: .
|
|
@@ -145,7 +146,8 @@ services:
|
|
|
145
146
|
- WATSONX_URL=${WATSONX_URL:-${WATSONX_ENDPOINT}}
|
|
146
147
|
- WATSONX_PROJECT_ID=${WATSONX_PROJECT_ID}
|
|
147
148
|
- OLLAMA_BASE_URL=${OLLAMA_ENDPOINT}
|
|
148
|
-
-
|
|
149
|
+
- LANGFLOW_CONFIG_DIR=/app/langflow-data
|
|
150
|
+
- LANGFLOW_DATABASE_URL=${LANGFLOW_DATABASE_URL:-sqlite:////app/langflow-data/langflow.db}
|
|
149
151
|
- LANGFLOW_SECRET_KEY=${LANGFLOW_SECRET_KEY}
|
|
150
152
|
- JWT=None
|
|
151
153
|
- OWNER=None
|