OpenRAG 0.2.5__tar.gz → 0.3.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.2.5/src/openrag.egg-info → openrag-0.3.0}/PKG-INFO +14 -2
- {openrag-0.2.5 → openrag-0.3.0}/README.md +3 -1
- {openrag-0.2.5 → openrag-0.3.0}/pyproject.toml +13 -1
- {openrag-0.2.5 → openrag-0.3.0}/src/agent.py +98 -46
- {openrag-0.2.5 → openrag-0.3.0}/src/api/settings.py +142 -16
- {openrag-0.2.5 → openrag-0.3.0}/src/api/v1/chat.py +22 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/config/settings.py +1 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/dependencies.py +7 -3
- {openrag-0.2.5 → openrag-0.3.0}/src/main.py +9 -16
- {openrag-0.2.5 → openrag-0.3.0}/src/models/processors.py +0 -16
- {openrag-0.2.5 → openrag-0.3.0/src/openrag.egg-info}/PKG-INFO +14 -2
- {openrag-0.2.5 → openrag-0.3.0}/src/services/chat_service.py +7 -2
- {openrag-0.2.5 → openrag-0.3.0}/src/services/flows_service.py +95 -178
- {openrag-0.2.5 → openrag-0.3.0}/src/services/langflow_file_service.py +26 -23
- {openrag-0.2.5 → openrag-0.3.0}/src/services/langflow_history_service.py +10 -2
- {openrag-0.2.5 → openrag-0.3.0}/src/services/search_service.py +1 -1
- {openrag-0.2.5 → openrag-0.3.0}/src/session_manager.py +12 -16
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/_assets/docker-compose.yml +8 -8
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/_assets/flows/ingestion_flow.json +2379 -1811
- openrag-0.3.0/src/tui/_assets/flows/openrag_agent.json +5230 -0
- openrag-0.3.0/src/tui/_assets/flows/openrag_nudges.json +4851 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/_assets/flows/openrag_url_mcp.json +1766 -1454
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/main.py +15 -2
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/managers/container_manager.py +100 -137
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/managers/env_manager.py +58 -14
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/utils/startup_checks.py +86 -4
- {openrag-0.2.5 → openrag-0.3.0}/src/utils/container_utils.py +7 -2
- {openrag-0.2.5 → openrag-0.3.0}/src/utils/langflow_headers.py +2 -2
- 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.0}/LICENSE +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/MANIFEST.in +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/setup.cfg +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/auth.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/chat.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/connector_router.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/connectors.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/docling.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/documents.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/flows.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/keys.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/knowledge_filter.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/langflow_files.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/models.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/nudges.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/oidc.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/provider_health.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/provider_validation.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/router.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/search.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/tasks.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/upload.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/v1/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/v1/documents.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/v1/knowledge_filters.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/v1/models.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/v1/search.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/api/v1/settings.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/auth_context.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/config/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/config/config_manager.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/config/model_constants.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/connectors/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/connectors/base.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/connectors/connection_manager.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/connectors/google_drive/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/connectors/google_drive/connector.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/connectors/google_drive/oauth.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/connectors/langflow_connector_service.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/connectors/onedrive/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/connectors/onedrive/connector.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/connectors/onedrive/oauth.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/connectors/service.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/connectors/sharepoint/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/connectors/sharepoint/connector.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/connectors/sharepoint/oauth.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/connectors/sharepoint/utils.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/models/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/models/tasks.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/openrag.egg-info/SOURCES.txt +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/openrag.egg-info/dependency_links.txt +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/openrag.egg-info/entry_points.txt +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/openrag.egg-info/requires.txt +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/openrag.egg-info/top_level.txt +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/services/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/services/api_key_service.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/services/auth_service.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/services/conversation_persistence_service.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/services/document_service.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/services/knowledge_filter_service.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/services/langflow_mcp_service.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/services/models_service.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/services/monitor_service.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/services/session_ownership_service.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/services/task_service.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/_assets/docker-compose.gpu.yml +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/_assets/flows/components/ollama_embedding.json +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/_assets/flows/components/ollama_llm.json +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/_assets/flows/components/ollama_llm_text.json +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/_assets/flows/components/watsonx_embedding.json +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/_assets/flows/components/watsonx_llm.json +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/_assets/flows/components/watsonx_llm_text.json +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/_assets/openrag-documents/docling.pdf +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/_assets/openrag-documents/ibm_anthropic.pdf +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/_assets/openrag-documents/openrag-documentation.pdf +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/_assets/openrag-documents/warmup_ocr.pdf +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/cli.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/config_fields.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/managers/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/managers/docling_manager.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/screens/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/screens/config.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/screens/diagnostics.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/screens/logs.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/screens/monitor.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/screens/welcome.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/utils/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/utils/clipboard.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/utils/platform.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/utils/validation.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/utils/version_check.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/widgets/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/widgets/command_modal.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/widgets/diagnostics_notification.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/widgets/error_notification.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/widgets/factory_reset_warning_modal.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/widgets/flow_backup_warning_modal.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/widgets/prune_options_modal.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/widgets/upgrade_instructions_modal.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/widgets/version_mismatch_warning_modal.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/tui/widgets/waves.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/utils/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/utils/acl_utils.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/utils/docling_client.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/utils/document_processing.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/utils/embedding_fields.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/utils/embeddings.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/utils/env_utils.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/utils/file_utils.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/utils/gpu_detection.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/utils/hash_utils.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/utils/langflow_utils.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/utils/logging_config.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/utils/opensearch_queries.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/utils/opensearch_utils.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/utils/paths.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/utils/telemetry/__init__.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/utils/telemetry/category.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/utils/telemetry/client.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/src/utils/telemetry/message_id.py +0 -0
- {openrag-0.2.5 → openrag-0.3.0}/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.0
|
|
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
|
|
@@ -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
|
|
|
@@ -58,6 +68,8 @@ OpenRAG is a comprehensive Retrieval-Augmented Generation platform that enables
|
|
|
58
68
|
|
|
59
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.
|
|
60
70
|
|
|
71
|
+
Check out the [documentation](https://docs.openr.ag/) or get started with the [quickstart](https://docs.openr.ag/quickstart).
|
|
72
|
+
|
|
61
73
|
Built with [Starlette](https://github.com/Kludex/starlette) 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
|
|
|
@@ -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
|
|
|
@@ -28,6 +28,8 @@ OpenRAG is a comprehensive Retrieval-Augmented Generation platform that enables
|
|
|
28
28
|
|
|
29
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
30
|
|
|
31
|
+
Check out the [documentation](https://docs.openr.ag/) or get started with the [quickstart](https://docs.openr.ag/quickstart).
|
|
32
|
+
|
|
31
33
|
Built with [Starlette](https://github.com/Kludex/starlette) 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
|
|
|
@@ -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.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"
|
|
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:
|
|
@@ -55,6 +55,10 @@ class SettingsUpdateBody(BaseModel):
|
|
|
55
55
|
watsonx_endpoint: Optional[str] = Field(None, min_length=1)
|
|
56
56
|
watsonx_project_id: Optional[str] = Field(None, min_length=1)
|
|
57
57
|
ollama_endpoint: Optional[str] = Field(None, min_length=1)
|
|
58
|
+
remove_ollama_config: Optional[bool] = None
|
|
59
|
+
remove_openai_config: Optional[bool] = None
|
|
60
|
+
remove_anthropic_config: Optional[bool] = None
|
|
61
|
+
remove_watsonx_config: Optional[bool] = None
|
|
58
62
|
|
|
59
63
|
|
|
60
64
|
class OnboardingBody(BaseModel):
|
|
@@ -168,6 +172,7 @@ class OnboardingResponse(BaseModel):
|
|
|
168
172
|
edited: bool
|
|
169
173
|
sample_data_ingested: bool
|
|
170
174
|
openrag_docs_filter_id: Optional[str] = None
|
|
175
|
+
task_id: Optional[str] = None
|
|
171
176
|
|
|
172
177
|
class DoclingConfig(BaseModel):
|
|
173
178
|
do_ocr: bool
|
|
@@ -360,6 +365,22 @@ async def get_settings(
|
|
|
360
365
|
)
|
|
361
366
|
|
|
362
367
|
|
|
368
|
+
def _first_configured_llm_provider(config, excluding: str) -> str:
|
|
369
|
+
"""Return the first configured LLM provider that isn't `excluding`."""
|
|
370
|
+
for p in ["openai", "anthropic", "watsonx", "ollama"]:
|
|
371
|
+
if p != excluding and getattr(config.providers, p).configured:
|
|
372
|
+
return p
|
|
373
|
+
return "openai"
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def _first_configured_embedding_provider(config, excluding: str) -> str:
|
|
377
|
+
"""Return the first configured embedding provider (openai/watsonx/ollama) that isn't `excluding`."""
|
|
378
|
+
for p in ["openai", "watsonx", "ollama"]:
|
|
379
|
+
if p != excluding and getattr(config.providers, p).configured:
|
|
380
|
+
return p
|
|
381
|
+
return "openai"
|
|
382
|
+
|
|
383
|
+
|
|
363
384
|
async def update_settings(
|
|
364
385
|
body: SettingsUpdateBody,
|
|
365
386
|
session_manager=Depends(get_session_manager),
|
|
@@ -672,6 +693,99 @@ async def update_settings(
|
|
|
672
693
|
config_updated = True
|
|
673
694
|
provider_updated = True
|
|
674
695
|
|
|
696
|
+
if body.remove_ollama_config:
|
|
697
|
+
other_providers_configured = (
|
|
698
|
+
current_config.providers.openai.configured
|
|
699
|
+
or current_config.providers.anthropic.configured
|
|
700
|
+
or current_config.providers.watsonx.configured
|
|
701
|
+
)
|
|
702
|
+
if not other_providers_configured:
|
|
703
|
+
return JSONResponse(
|
|
704
|
+
{"error": "Cannot remove Ollama configuration: configure another model provider first."},
|
|
705
|
+
status_code=400,
|
|
706
|
+
)
|
|
707
|
+
current_config.providers.ollama.endpoint = ""
|
|
708
|
+
current_config.providers.ollama.configured = False
|
|
709
|
+
if current_config.agent.llm_provider == "ollama":
|
|
710
|
+
current_config.agent.llm_provider = _first_configured_llm_provider(current_config, "ollama")
|
|
711
|
+
current_config.agent.llm_model = ""
|
|
712
|
+
if current_config.knowledge.embedding_provider == "ollama":
|
|
713
|
+
current_config.knowledge.embedding_provider = _first_configured_embedding_provider(current_config, "ollama")
|
|
714
|
+
current_config.knowledge.embedding_model = ""
|
|
715
|
+
config_updated = True
|
|
716
|
+
provider_updated = True
|
|
717
|
+
|
|
718
|
+
if body.remove_openai_config:
|
|
719
|
+
other_providers_configured = (
|
|
720
|
+
current_config.providers.anthropic.configured
|
|
721
|
+
or current_config.providers.watsonx.configured
|
|
722
|
+
or current_config.providers.ollama.configured
|
|
723
|
+
)
|
|
724
|
+
if not other_providers_configured:
|
|
725
|
+
return JSONResponse(
|
|
726
|
+
{"error": "Cannot remove OpenAI configuration: configure another model provider first."},
|
|
727
|
+
status_code=400,
|
|
728
|
+
)
|
|
729
|
+
current_config.providers.openai.api_key = ""
|
|
730
|
+
current_config.providers.openai.configured = False
|
|
731
|
+
if current_config.agent.llm_provider == "openai":
|
|
732
|
+
fb = _first_configured_llm_provider(current_config, "openai")
|
|
733
|
+
current_config.agent.llm_provider = fb
|
|
734
|
+
current_config.agent.llm_model = ""
|
|
735
|
+
if current_config.knowledge.embedding_provider == "openai":
|
|
736
|
+
fb = _first_configured_embedding_provider(current_config, "openai")
|
|
737
|
+
current_config.knowledge.embedding_provider = fb
|
|
738
|
+
current_config.knowledge.embedding_model = ""
|
|
739
|
+
config_updated = True
|
|
740
|
+
provider_updated = True
|
|
741
|
+
|
|
742
|
+
if body.remove_anthropic_config:
|
|
743
|
+
other_providers_configured = (
|
|
744
|
+
current_config.providers.openai.configured
|
|
745
|
+
or current_config.providers.watsonx.configured
|
|
746
|
+
or current_config.providers.ollama.configured
|
|
747
|
+
)
|
|
748
|
+
if not other_providers_configured:
|
|
749
|
+
return JSONResponse(
|
|
750
|
+
{"error": "Cannot remove Anthropic configuration: configure another model provider first."},
|
|
751
|
+
status_code=400,
|
|
752
|
+
)
|
|
753
|
+
current_config.providers.anthropic.api_key = ""
|
|
754
|
+
current_config.providers.anthropic.configured = False
|
|
755
|
+
if current_config.agent.llm_provider == "anthropic":
|
|
756
|
+
fb = _first_configured_llm_provider(current_config, "anthropic")
|
|
757
|
+
current_config.agent.llm_provider = fb
|
|
758
|
+
current_config.agent.llm_model = ""
|
|
759
|
+
# Anthropic is not a valid embedding provider; no embedding reset needed
|
|
760
|
+
config_updated = True
|
|
761
|
+
provider_updated = True
|
|
762
|
+
|
|
763
|
+
if body.remove_watsonx_config:
|
|
764
|
+
other_providers_configured = (
|
|
765
|
+
current_config.providers.openai.configured
|
|
766
|
+
or current_config.providers.anthropic.configured
|
|
767
|
+
or current_config.providers.ollama.configured
|
|
768
|
+
)
|
|
769
|
+
if not other_providers_configured:
|
|
770
|
+
return JSONResponse(
|
|
771
|
+
{"error": "Cannot remove IBM watsonx.ai configuration: configure another model provider first."},
|
|
772
|
+
status_code=400,
|
|
773
|
+
)
|
|
774
|
+
current_config.providers.watsonx.api_key = ""
|
|
775
|
+
current_config.providers.watsonx.endpoint = ""
|
|
776
|
+
current_config.providers.watsonx.project_id = ""
|
|
777
|
+
current_config.providers.watsonx.configured = False
|
|
778
|
+
if current_config.agent.llm_provider == "watsonx":
|
|
779
|
+
fb = _first_configured_llm_provider(current_config, "watsonx")
|
|
780
|
+
current_config.agent.llm_provider = fb
|
|
781
|
+
current_config.agent.llm_model = ""
|
|
782
|
+
if current_config.knowledge.embedding_provider == "watsonx":
|
|
783
|
+
fb = _first_configured_embedding_provider(current_config, "watsonx")
|
|
784
|
+
current_config.knowledge.embedding_provider = fb
|
|
785
|
+
current_config.knowledge.embedding_model = ""
|
|
786
|
+
config_updated = True
|
|
787
|
+
provider_updated = True
|
|
788
|
+
|
|
675
789
|
if provider_updated:
|
|
676
790
|
await TelemetryClient.send_event(
|
|
677
791
|
Category.SETTINGS_OPERATIONS,
|
|
@@ -692,7 +806,7 @@ async def update_settings(
|
|
|
692
806
|
# Update Langflow global variables and model values if provider settings changed
|
|
693
807
|
await clients.refresh_patched_client()
|
|
694
808
|
|
|
695
|
-
if should_validate:
|
|
809
|
+
if should_validate or provider_updated:
|
|
696
810
|
try:
|
|
697
811
|
flows_service = _get_flows_service()
|
|
698
812
|
|
|
@@ -705,8 +819,8 @@ async def update_settings(
|
|
|
705
819
|
current_config, session_manager
|
|
706
820
|
)
|
|
707
821
|
|
|
708
|
-
# Update model values if provider or model changed
|
|
709
|
-
if body.llm_provider is not None or body.llm_model is not None or body.embedding_provider is not None or body.embedding_model is not None:
|
|
822
|
+
# Update model values if provider or model changed (including removals that trigger fallback)
|
|
823
|
+
if body.llm_provider is not None or body.llm_model is not None or body.embedding_provider is not None or body.embedding_model is not None or provider_updated:
|
|
710
824
|
await _update_langflow_model_values(current_config, flows_service)
|
|
711
825
|
|
|
712
826
|
except Exception as e:
|
|
@@ -975,6 +1089,8 @@ async def onboarding(
|
|
|
975
1089
|
)
|
|
976
1090
|
raise
|
|
977
1091
|
|
|
1092
|
+
task_id = None
|
|
1093
|
+
|
|
978
1094
|
# Initialize the OpenSearch index if embedding model is configured
|
|
979
1095
|
if body.embedding_model or body.embedding_provider:
|
|
980
1096
|
try:
|
|
@@ -1004,7 +1120,7 @@ async def onboarding(
|
|
|
1004
1120
|
# Import the function here to avoid circular imports
|
|
1005
1121
|
from main import ingest_default_documents_when_ready
|
|
1006
1122
|
|
|
1007
|
-
await ingest_default_documents_when_ready(
|
|
1123
|
+
task_id = await ingest_default_documents_when_ready(
|
|
1008
1124
|
document_service,
|
|
1009
1125
|
task_service,
|
|
1010
1126
|
langflow_file_service,
|
|
@@ -1094,6 +1210,7 @@ async def onboarding(
|
|
|
1094
1210
|
edited=True, # Confirm that config is now marked as edited
|
|
1095
1211
|
sample_data_ingested=should_ingest_sample_data,
|
|
1096
1212
|
openrag_docs_filter_id=openrag_docs_filter_id,
|
|
1213
|
+
task_id=task_id,
|
|
1097
1214
|
)
|
|
1098
1215
|
|
|
1099
1216
|
except Exception as e:
|
|
@@ -1121,7 +1238,7 @@ async def _create_openrag_docs_filter(
|
|
|
1121
1238
|
return None
|
|
1122
1239
|
|
|
1123
1240
|
# Get JWT token
|
|
1124
|
-
|
|
1241
|
+
jwt_token = user.jwt_token
|
|
1125
1242
|
|
|
1126
1243
|
# In no-auth mode, set owner to None so filter is visible to all users
|
|
1127
1244
|
# In auth mode, use the actual user as owner
|
|
@@ -1192,6 +1309,12 @@ async def _update_langflow_global_variables(config):
|
|
|
1192
1309
|
)
|
|
1193
1310
|
logger.info("Set WATSONX_PROJECT_ID global variable in Langflow")
|
|
1194
1311
|
|
|
1312
|
+
if config.providers.watsonx.endpoint:
|
|
1313
|
+
await clients._create_langflow_global_variable(
|
|
1314
|
+
"WATSONX_URL", config.providers.watsonx.endpoint, modify=True
|
|
1315
|
+
)
|
|
1316
|
+
logger.info("Set WATSONX_URL global variable in Langflow")
|
|
1317
|
+
|
|
1195
1318
|
# OpenAI global variables
|
|
1196
1319
|
if config.providers.openai.api_key:
|
|
1197
1320
|
await clients._create_langflow_global_variable(
|
|
@@ -1208,11 +1331,19 @@ async def _update_langflow_global_variables(config):
|
|
|
1208
1331
|
|
|
1209
1332
|
# Ollama global variables
|
|
1210
1333
|
if config.providers.ollama.endpoint:
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1334
|
+
|
|
1335
|
+
try:
|
|
1336
|
+
endpoint = transform_localhost_url(config.providers.ollama.endpoint, is_langflow=True, is_podman=True)
|
|
1337
|
+
await clients._create_langflow_global_variable(
|
|
1338
|
+
"OLLAMA_BASE_URL", endpoint, modify=True
|
|
1339
|
+
)
|
|
1340
|
+
logger.info("Set OLLAMA_BASE_URL global variable in Langflow (Podman)")
|
|
1341
|
+
except Exception:
|
|
1342
|
+
endpoint = transform_localhost_url(config.providers.ollama.endpoint, is_langflow=True, is_podman=False)
|
|
1343
|
+
await clients._create_langflow_global_variable(
|
|
1344
|
+
"OLLAMA_BASE_URL", endpoint, modify=True
|
|
1345
|
+
)
|
|
1346
|
+
logger.info("Set OLLAMA_BASE_URL global variable in Langflow (Docker)")
|
|
1216
1347
|
|
|
1217
1348
|
if config.knowledge.embedding_model:
|
|
1218
1349
|
await clients._create_langflow_global_variable(
|
|
@@ -1269,13 +1400,10 @@ async def _update_langflow_model_values(config, flows_service):
|
|
|
1269
1400
|
try:
|
|
1270
1401
|
# Update LLM model values
|
|
1271
1402
|
llm_provider = config.agent.llm_provider.lower()
|
|
1272
|
-
llm_provider_config = config.get_llm_provider_config()
|
|
1273
|
-
llm_endpoint = getattr(llm_provider_config, "endpoint", None)
|
|
1274
1403
|
|
|
1275
1404
|
await flows_service.change_langflow_model_value(
|
|
1276
1405
|
llm_provider,
|
|
1277
1406
|
llm_model=config.agent.llm_model,
|
|
1278
|
-
endpoint=llm_endpoint,
|
|
1279
1407
|
)
|
|
1280
1408
|
logger.info(
|
|
1281
1409
|
f"Successfully updated Langflow flows for LLM provider {llm_provider}"
|
|
@@ -1283,13 +1411,10 @@ async def _update_langflow_model_values(config, flows_service):
|
|
|
1283
1411
|
|
|
1284
1412
|
# Update embedding model values
|
|
1285
1413
|
embedding_provider = config.knowledge.embedding_provider.lower()
|
|
1286
|
-
embedding_provider_config = config.get_embedding_provider_config()
|
|
1287
|
-
embedding_endpoint = getattr(embedding_provider_config, "endpoint", None)
|
|
1288
1414
|
|
|
1289
1415
|
await flows_service.change_langflow_model_value(
|
|
1290
1416
|
embedding_provider,
|
|
1291
1417
|
embedding_model=config.knowledge.embedding_model,
|
|
1292
|
-
endpoint=embedding_endpoint,
|
|
1293
1418
|
)
|
|
1294
1419
|
logger.info(
|
|
1295
1420
|
f"Successfully updated Langflow flows for embedding provider {embedding_provider}"
|
|
@@ -1517,6 +1642,7 @@ async def rollback_onboarding(
|
|
|
1517
1642
|
|
|
1518
1643
|
# Mark config as not edited so user can go through onboarding again
|
|
1519
1644
|
current_config.edited = False
|
|
1645
|
+
current_config.onboarding.current_step = 0
|
|
1520
1646
|
|
|
1521
1647
|
# Save the rolled back configuration manually to avoid save_config_file setting edited=True
|
|
1522
1648
|
try:
|
|
@@ -28,6 +28,21 @@ class ChatV1Body(BaseModel):
|
|
|
28
28
|
filter_id: Optional[str] = None
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
def _extract_sources(item: dict) -> list[dict]:
|
|
32
|
+
"""Extract sources from a retrieval tool call item."""
|
|
33
|
+
sources = []
|
|
34
|
+
for result in item.get("results", []):
|
|
35
|
+
if isinstance(result, dict) and "text" in result:
|
|
36
|
+
sources.append({
|
|
37
|
+
"filename": result.get("filename", ""),
|
|
38
|
+
"text": result.get("text", ""),
|
|
39
|
+
"score": result.get("score", 0),
|
|
40
|
+
"page": result.get("page"),
|
|
41
|
+
"mimetype": result.get("mimetype"),
|
|
42
|
+
})
|
|
43
|
+
return sources
|
|
44
|
+
|
|
45
|
+
|
|
31
46
|
async def _transform_stream_to_sse(raw_stream, chat_id_container: dict):
|
|
32
47
|
"""Transform raw Langflow streaming format to clean SSE events for v1 API."""
|
|
33
48
|
full_text = ""
|
|
@@ -64,6 +79,13 @@ async def _transform_stream_to_sse(raw_stream, chat_id_container: dict):
|
|
|
64
79
|
full_text += delta_text
|
|
65
80
|
yield f"data: {json.dumps({'type': 'content', 'delta': delta_text})}\n\n"
|
|
66
81
|
|
|
82
|
+
# Emit sources from retrieval tool calls
|
|
83
|
+
item = chunk_data.get("item", {})
|
|
84
|
+
if item.get("type") in ("retrieval_call", "tool_call") and item.get("results"):
|
|
85
|
+
sources = _extract_sources(item)
|
|
86
|
+
if sources:
|
|
87
|
+
yield f"data: {json.dumps({'type': 'sources', 'sources': sources})}\n\n"
|
|
88
|
+
|
|
67
89
|
if not chat_id:
|
|
68
90
|
chat_id = chunk_data.get("id") or chunk_data.get("response_id")
|
|
69
91
|
|
|
@@ -162,6 +162,7 @@ def get_optional_user(
|
|
|
162
162
|
async def get_api_key_user_async(
|
|
163
163
|
request: Request,
|
|
164
164
|
api_key_service=Depends(get_api_key_service),
|
|
165
|
+
session_manager=Depends(get_session_manager),
|
|
165
166
|
) -> User:
|
|
166
167
|
"""
|
|
167
168
|
Async dependency: require API key authentication.
|
|
@@ -208,9 +209,12 @@ async def get_api_key_user_async(
|
|
|
208
209
|
provider="api_key",
|
|
209
210
|
)
|
|
210
211
|
|
|
211
|
-
#
|
|
212
|
-
|
|
213
|
-
|
|
212
|
+
# Register the API key user so get_effective_jwt_token can find them
|
|
213
|
+
if user.user_id not in session_manager.users:
|
|
214
|
+
session_manager.users[user.user_id] = user
|
|
215
|
+
|
|
216
|
+
effective_token = session_manager.get_effective_jwt_token(user.user_id, None)
|
|
217
|
+
user_with_token = dataclasses.replace(user, jwt_token=effective_token)
|
|
214
218
|
|
|
215
219
|
request.state.user = user_with_token
|
|
216
220
|
request.state.api_key_id = user_info["key_id"]
|