OpenRAG 0.1.31__tar.gz → 0.2.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.
Files changed (155) hide show
  1. {openrag-0.1.31/src/openrag.egg-info → openrag-0.2.0}/PKG-INFO +24 -21
  2. {openrag-0.1.31 → openrag-0.2.0}/README.md +21 -19
  3. {openrag-0.1.31 → openrag-0.2.0}/pyproject.toml +3 -2
  4. {openrag-0.1.31 → openrag-0.2.0}/src/agent.py +115 -19
  5. {openrag-0.1.31 → openrag-0.2.0}/src/api/auth.py +6 -0
  6. {openrag-0.1.31 → openrag-0.2.0}/src/api/chat.py +6 -0
  7. {openrag-0.1.31 → openrag-0.2.0}/src/api/connectors.py +6 -0
  8. openrag-0.2.0/src/api/keys.py +170 -0
  9. {openrag-0.1.31 → openrag-0.2.0}/src/api/models.py +75 -23
  10. {openrag-0.1.31 → openrag-0.2.0}/src/api/nudges.py +28 -0
  11. openrag-0.2.0/src/api/provider_health.py +230 -0
  12. openrag-0.2.0/src/api/provider_validation.py +818 -0
  13. {openrag-0.1.31 → openrag-0.2.0}/src/api/router.py +11 -8
  14. openrag-0.2.0/src/api/settings.py +1610 -0
  15. {openrag-0.1.31 → openrag-0.2.0}/src/api/tasks.py +3 -0
  16. openrag-0.2.0/src/api/v1/__init__.py +6 -0
  17. openrag-0.2.0/src/api/v1/chat.py +301 -0
  18. openrag-0.2.0/src/api/v1/documents.py +150 -0
  19. openrag-0.2.0/src/api/v1/knowledge_filters.py +63 -0
  20. openrag-0.2.0/src/api/v1/search.py +108 -0
  21. openrag-0.2.0/src/api/v1/settings.py +86 -0
  22. openrag-0.2.0/src/api_key_middleware.py +133 -0
  23. openrag-0.2.0/src/config/config_manager.py +299 -0
  24. {openrag-0.1.31 → openrag-0.2.0}/src/config/settings.py +282 -212
  25. {openrag-0.1.31 → openrag-0.2.0}/src/connectors/connection_manager.py +3 -1
  26. {openrag-0.1.31 → openrag-0.2.0}/src/connectors/google_drive/connector.py +49 -11
  27. {openrag-0.1.31 → openrag-0.2.0}/src/connectors/google_drive/oauth.py +18 -2
  28. {openrag-0.1.31 → openrag-0.2.0}/src/connectors/onedrive/connector.py +81 -7
  29. {openrag-0.1.31 → openrag-0.2.0}/src/connectors/sharepoint/connector.py +87 -9
  30. {openrag-0.1.31 → openrag-0.2.0}/src/main.py +434 -11
  31. {openrag-0.1.31 → openrag-0.2.0}/src/models/processors.py +42 -9
  32. {openrag-0.1.31 → openrag-0.2.0/src/openrag.egg-info}/PKG-INFO +24 -21
  33. {openrag-0.1.31 → openrag-0.2.0}/src/openrag.egg-info/SOURCES.txt +27 -6
  34. {openrag-0.1.31 → openrag-0.2.0}/src/openrag.egg-info/requires.txt +2 -1
  35. {openrag-0.1.31 → openrag-0.2.0}/src/openrag.egg-info/top_level.txt +1 -0
  36. openrag-0.2.0/src/services/api_key_service.py +372 -0
  37. {openrag-0.1.31 → openrag-0.2.0}/src/services/auth_service.py +12 -2
  38. {openrag-0.1.31 → openrag-0.2.0}/src/services/chat_service.py +115 -9
  39. {openrag-0.1.31 → openrag-0.2.0}/src/services/conversation_persistence_service.py +23 -14
  40. {openrag-0.1.31 → openrag-0.2.0}/src/services/document_service.py +37 -19
  41. openrag-0.2.0/src/services/flows_service.py +1451 -0
  42. {openrag-0.1.31 → openrag-0.2.0}/src/services/langflow_file_service.py +31 -4
  43. {openrag-0.1.31 → openrag-0.2.0}/src/services/langflow_history_service.py +12 -11
  44. {openrag-0.1.31 → openrag-0.2.0}/src/services/models_service.py +113 -44
  45. {openrag-0.1.31 → openrag-0.2.0}/src/services/search_service.py +31 -6
  46. {openrag-0.1.31 → openrag-0.2.0}/src/services/session_ownership_service.py +4 -2
  47. {openrag-0.1.31 → openrag-0.2.0}/src/services/task_service.py +80 -7
  48. openrag-0.2.0/src/tui/__init__.py +5 -0
  49. openrag-0.2.0/src/tui/_assets/docker-compose.gpu.yml +7 -0
  50. {openrag-0.1.31 → openrag-0.2.0}/src/tui/_assets/docker-compose.yml +41 -24
  51. {openrag-0.1.31 → openrag-0.2.0}/src/tui/_assets/flows/components/ollama_embedding.json +2 -2
  52. {openrag-0.1.31 → openrag-0.2.0}/src/tui/_assets/flows/components/ollama_llm.json +2 -2
  53. {openrag-0.1.31 → openrag-0.2.0}/src/tui/_assets/flows/components/ollama_llm_text.json +2 -2
  54. openrag-0.2.0/src/tui/_assets/flows/ingestion_flow.json +5758 -0
  55. openrag-0.2.0/src/tui/_assets/flows/openrag_agent.json +4795 -0
  56. openrag-0.2.0/src/tui/_assets/flows/openrag_nudges.json +4122 -0
  57. openrag-0.2.0/src/tui/_assets/flows/openrag_url_mcp.json +6802 -0
  58. openrag-0.2.0/src/tui/_assets/openrag-documents/openrag-documentation.pdf +0 -0
  59. {openrag-0.1.31 → openrag-0.2.0}/src/tui/main.py +229 -9
  60. {openrag-0.1.31 → openrag-0.2.0}/src/tui/managers/container_manager.py +782 -64
  61. {openrag-0.1.31 → openrag-0.2.0}/src/tui/managers/docling_manager.py +52 -0
  62. openrag-0.2.0/src/tui/managers/env_manager.py +700 -0
  63. {openrag-0.1.31 → openrag-0.2.0}/src/tui/screens/config.py +365 -38
  64. {openrag-0.1.31 → openrag-0.2.0}/src/tui/screens/diagnostics.py +2 -2
  65. {openrag-0.1.31 → openrag-0.2.0}/src/tui/screens/monitor.py +354 -46
  66. {openrag-0.1.31 → openrag-0.2.0}/src/tui/screens/welcome.py +240 -36
  67. {openrag-0.1.31 → openrag-0.2.0}/src/tui/utils/validation.py +21 -15
  68. openrag-0.2.0/src/tui/utils/version_check.py +175 -0
  69. openrag-0.2.0/src/tui/widgets/__init__.py +7 -0
  70. {openrag-0.1.31 → openrag-0.2.0}/src/tui/widgets/command_modal.py +37 -4
  71. {openrag-0.1.31 → openrag-0.2.0}/src/tui/widgets/diagnostics_notification.py +2 -2
  72. {openrag-0.1.31 → openrag-0.2.0}/src/tui/widgets/error_notification.py +2 -2
  73. openrag-0.2.0/src/tui/widgets/factory_reset_warning_modal.py +102 -0
  74. openrag-0.2.0/src/tui/widgets/flow_backup_warning_modal.py +125 -0
  75. openrag-0.2.0/src/tui/widgets/prune_options_modal.py +106 -0
  76. openrag-0.2.0/src/tui/widgets/upgrade_instructions_modal.py +112 -0
  77. openrag-0.2.0/src/tui/widgets/version_mismatch_warning_modal.py +114 -0
  78. {openrag-0.1.31 → openrag-0.2.0}/src/utils/document_processing.py +76 -0
  79. openrag-0.2.0/src/utils/langflow_headers.py +71 -0
  80. openrag-0.2.0/src/utils/paths.py +85 -0
  81. openrag-0.2.0/src/utils/telemetry/__init__.py +8 -0
  82. openrag-0.2.0/src/utils/telemetry/category.py +45 -0
  83. openrag-0.2.0/src/utils/telemetry/client.py +409 -0
  84. openrag-0.2.0/src/utils/telemetry/message_id.py +203 -0
  85. openrag-0.1.31/src/api/provider_health.py +0 -117
  86. openrag-0.1.31/src/api/provider_validation.py +0 -437
  87. openrag-0.1.31/src/api/settings.py +0 -944
  88. openrag-0.1.31/src/config/config_manager.py +0 -214
  89. openrag-0.1.31/src/services/flows_service.py +0 -906
  90. openrag-0.1.31/src/tui/__init__.py +0 -8
  91. openrag-0.1.31/src/tui/_assets/docker-compose-cpu.yml +0 -126
  92. openrag-0.1.31/src/tui/_assets/documents/openrag-documentation.pdf +0 -0
  93. openrag-0.1.31/src/tui/_assets/flows/ingestion_flow.json +0 -4199
  94. openrag-0.1.31/src/tui/_assets/flows/openrag_agent.json +0 -2875
  95. openrag-0.1.31/src/tui/_assets/flows/openrag_nudges.json +0 -2350
  96. openrag-0.1.31/src/tui/_assets/flows/openrag_url_mcp.json +0 -3621
  97. openrag-0.1.31/src/tui/managers/env_manager.py +0 -445
  98. openrag-0.1.31/src/tui/widgets/__init__.py +0 -3
  99. {openrag-0.1.31 → openrag-0.2.0}/LICENSE +0 -0
  100. {openrag-0.1.31 → openrag-0.2.0}/MANIFEST.in +0 -0
  101. {openrag-0.1.31 → openrag-0.2.0}/setup.cfg +0 -0
  102. {openrag-0.1.31 → openrag-0.2.0}/src/api/__init__.py +0 -0
  103. {openrag-0.1.31 → openrag-0.2.0}/src/api/connector_router.py +0 -0
  104. {openrag-0.1.31 → openrag-0.2.0}/src/api/docling.py +0 -0
  105. {openrag-0.1.31 → openrag-0.2.0}/src/api/documents.py +0 -0
  106. {openrag-0.1.31 → openrag-0.2.0}/src/api/flows.py +0 -0
  107. {openrag-0.1.31 → openrag-0.2.0}/src/api/knowledge_filter.py +0 -0
  108. {openrag-0.1.31 → openrag-0.2.0}/src/api/langflow_files.py +0 -0
  109. {openrag-0.1.31 → openrag-0.2.0}/src/api/oidc.py +0 -0
  110. {openrag-0.1.31 → openrag-0.2.0}/src/api/search.py +0 -0
  111. {openrag-0.1.31 → openrag-0.2.0}/src/api/upload.py +0 -0
  112. {openrag-0.1.31 → openrag-0.2.0}/src/auth_context.py +0 -0
  113. {openrag-0.1.31 → openrag-0.2.0}/src/auth_middleware.py +0 -0
  114. {openrag-0.1.31 → openrag-0.2.0}/src/config/__init__.py +0 -0
  115. {openrag-0.1.31 → openrag-0.2.0}/src/connectors/__init__.py +0 -0
  116. {openrag-0.1.31 → openrag-0.2.0}/src/connectors/base.py +0 -0
  117. {openrag-0.1.31 → openrag-0.2.0}/src/connectors/google_drive/__init__.py +0 -0
  118. {openrag-0.1.31 → openrag-0.2.0}/src/connectors/langflow_connector_service.py +0 -0
  119. {openrag-0.1.31 → openrag-0.2.0}/src/connectors/onedrive/__init__.py +0 -0
  120. {openrag-0.1.31 → openrag-0.2.0}/src/connectors/onedrive/oauth.py +0 -0
  121. {openrag-0.1.31 → openrag-0.2.0}/src/connectors/service.py +0 -0
  122. {openrag-0.1.31 → openrag-0.2.0}/src/connectors/sharepoint/__init__.py +0 -0
  123. {openrag-0.1.31 → openrag-0.2.0}/src/connectors/sharepoint/oauth.py +0 -0
  124. {openrag-0.1.31 → openrag-0.2.0}/src/models/__init__.py +0 -0
  125. {openrag-0.1.31 → openrag-0.2.0}/src/models/tasks.py +0 -0
  126. {openrag-0.1.31 → openrag-0.2.0}/src/openrag.egg-info/dependency_links.txt +0 -0
  127. {openrag-0.1.31 → openrag-0.2.0}/src/openrag.egg-info/entry_points.txt +0 -0
  128. {openrag-0.1.31 → openrag-0.2.0}/src/services/__init__.py +0 -0
  129. {openrag-0.1.31 → openrag-0.2.0}/src/services/knowledge_filter_service.py +0 -0
  130. {openrag-0.1.31 → openrag-0.2.0}/src/services/langflow_mcp_service.py +0 -0
  131. {openrag-0.1.31 → openrag-0.2.0}/src/services/monitor_service.py +0 -0
  132. {openrag-0.1.31 → openrag-0.2.0}/src/session_manager.py +0 -0
  133. {openrag-0.1.31 → openrag-0.2.0}/src/tui/_assets/flows/components/watsonx_embedding.json +0 -0
  134. {openrag-0.1.31 → openrag-0.2.0}/src/tui/_assets/flows/components/watsonx_llm.json +0 -0
  135. {openrag-0.1.31 → openrag-0.2.0}/src/tui/_assets/flows/components/watsonx_llm_text.json +0 -0
  136. {openrag-0.1.31/src/tui/_assets/documents → openrag-0.2.0/src/tui/_assets/openrag-documents}/docling.pdf +0 -0
  137. {openrag-0.1.31/src/tui/_assets/documents → openrag-0.2.0/src/tui/_assets/openrag-documents}/ibm_anthropic.pdf +0 -0
  138. {openrag-0.1.31/src/tui/_assets/documents → openrag-0.2.0/src/tui/_assets/openrag-documents}/warmup_ocr.pdf +0 -0
  139. {openrag-0.1.31 → openrag-0.2.0}/src/tui/managers/__init__.py +0 -0
  140. {openrag-0.1.31 → openrag-0.2.0}/src/tui/screens/__init__.py +0 -0
  141. {openrag-0.1.31 → openrag-0.2.0}/src/tui/screens/logs.py +0 -0
  142. {openrag-0.1.31 → openrag-0.2.0}/src/tui/utils/__init__.py +0 -0
  143. {openrag-0.1.31 → openrag-0.2.0}/src/tui/utils/clipboard.py +0 -0
  144. {openrag-0.1.31 → openrag-0.2.0}/src/tui/utils/platform.py +0 -0
  145. {openrag-0.1.31 → openrag-0.2.0}/src/tui/widgets/waves.py +0 -0
  146. {openrag-0.1.31 → openrag-0.2.0}/src/utils/__init__.py +0 -0
  147. {openrag-0.1.31 → openrag-0.2.0}/src/utils/container_utils.py +0 -0
  148. {openrag-0.1.31 → openrag-0.2.0}/src/utils/embedding_fields.py +0 -0
  149. {openrag-0.1.31 → openrag-0.2.0}/src/utils/embeddings.py +0 -0
  150. {openrag-0.1.31 → openrag-0.2.0}/src/utils/file_utils.py +0 -0
  151. {openrag-0.1.31 → openrag-0.2.0}/src/utils/gpu_detection.py +0 -0
  152. {openrag-0.1.31 → openrag-0.2.0}/src/utils/hash_utils.py +0 -0
  153. {openrag-0.1.31 → openrag-0.2.0}/src/utils/logging_config.py +0 -0
  154. {openrag-0.1.31 → openrag-0.2.0}/src/utils/opensearch_queries.py +0 -0
  155. {openrag-0.1.31 → openrag-0.2.0}/src/utils/process_pool.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openrag
3
- Version: 0.1.31
3
+ Version: 0.2.0
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.13
6
6
  Description-Content-Type: text/markdown
@@ -30,7 +30,8 @@ Requires-Dist: textual-fspicker>=0.6.0
30
30
  Requires-Dist: structlog>=25.4.0
31
31
  Requires-Dist: docling-serve==1.5.0
32
32
  Requires-Dist: docling-core==2.48.1
33
- Requires-Dist: easyocr>=1.7.1
33
+ Requires-Dist: easyocr>=1.7.1; sys_platform != "darwin"
34
+ Requires-Dist: zxcvbn>=4.5.0
34
35
  Dynamic: license-file
35
36
 
36
37
  <div align="center">
@@ -53,27 +54,29 @@ OpenRAG is a comprehensive Retrieval-Augmented Generation platform that enables
53
54
  </div>
54
55
  <div align="center">
55
56
  <a href="#quickstart" style="color: #0366d6;">Quickstart</a> &nbsp;&nbsp;|&nbsp;&nbsp;
56
- <a href="#tui-interface" style="color: #0366d6;">TUI Interface</a> &nbsp;&nbsp;|&nbsp;&nbsp;
57
- <a href="#docker-deployment" style="color: #0366d6;">Docker Deployment</a> &nbsp;&nbsp;|&nbsp;&nbsp;
57
+ <a href="#install-python-package" style="color: #0366d6;">Python package</a> &nbsp;&nbsp;|&nbsp;&nbsp;
58
+ <a href="#docker-or-podman-installation" style="color: #0366d6;">Docker or Podman</a> &nbsp;&nbsp;|&nbsp;&nbsp;
58
59
  <a href="#development" style="color: #0366d6;">Development</a> &nbsp;&nbsp;|&nbsp;&nbsp;
59
60
  <a href="#troubleshooting" style="color: #0366d6;">Troubleshooting</a>
60
61
  </div>
61
62
 
62
63
  ## Quickstart
63
64
 
64
- To quickly run OpenRAG without creating or modifying any project files, use `uvx`:
65
+ To run OpenRAG without creating or modifying any project files, use `uvx`:
65
66
 
66
67
  ```bash
67
68
  uvx openrag
68
69
  ```
69
- This runs OpenRAG without installing it to your project or globally.
70
- To run a specific version of OpenRAG, add the version to the command, such as: `uvx --from openrag==0.1.25 openrag`.
70
+
71
+ This command runs OpenRAG without installing it to your project or globally.
72
+
73
+ To run a specific version of OpenRAG, run `uvx --from openrag==VERSION openrag`.
71
74
 
72
75
  ## Install Python package
73
76
 
74
- To first set up a project and then install the OpenRAG Python package, do the following:
77
+ To add the OpenRAG Python package to a Python project, use `uv`:
75
78
 
76
- 1. Create a new project with a virtual environment using `uv init`.
79
+ 1. Create a new project with a virtual environment using `uv init`:
77
80
 
78
81
  ```bash
79
82
  uv init YOUR_PROJECT_NAME
@@ -83,33 +86,33 @@ To first set up a project and then install the OpenRAG Python package, do the fo
83
86
  The `(venv)` prompt doesn't change, but `uv` commands will automatically use the project's virtual environment.
84
87
  For more information on virtual environments, see the [uv documentation](https://docs.astral.sh/uv/pip/environments).
85
88
 
86
- 2. Add OpenRAG to your project.
89
+ 2. Add OpenRAG to your project:
90
+
87
91
  ```bash
88
92
  uv add openrag
89
93
  ```
90
94
 
91
- To add a specific version of OpenRAG:
92
- ```bash
93
- uv add openrag==0.1.25
94
- ```
95
+ To add a specific version of OpenRAG, run `uv add openrag==VERSION`.
96
+
97
+ 3. Start the OpenRAG terminal user interface (TUI):
95
98
 
96
- 3. Start the OpenRAG TUI.
97
99
  ```bash
98
100
  uv run openrag
99
101
  ```
100
102
 
101
103
  4. Continue with the [Quickstart](https://docs.openr.ag/quickstart).
102
104
 
103
- For the full TUI installation guide, see [TUI](https://docs.openr.ag/install).
105
+ For all installation options, see the [OpenRAG installation guide](https://docs.openr.ag/install).
104
106
 
105
107
  ## Docker or Podman installation
106
108
 
107
- For more information, see [Install OpenRAG containers](https://docs.openr.ag/get-started/docker).
108
-
109
- ## Troubleshooting
110
-
111
- For common issues and fixes, see [Troubleshoot](https://docs.openr.ag/support/troubleshoot).
109
+ By default, OpenRAG automatically starts the required containers and helps you manage them.
110
+ To install OpenRAG with self-managed containers, see the [OpenRAG installation guide](https://docs.openr.ag/docker).
112
111
 
113
112
  ## Development
114
113
 
115
114
  For developers wanting to contribute to OpenRAG or set up a development environment, see [CONTRIBUTING.md](CONTRIBUTING.md).
115
+
116
+ ## Troubleshooting
117
+
118
+ For common issues and fixes, see [Troubleshoot OpenRAG](https://docs.openr.ag/support/troubleshoot).
@@ -18,27 +18,29 @@ OpenRAG is a comprehensive Retrieval-Augmented Generation platform that enables
18
18
  </div>
19
19
  <div align="center">
20
20
  <a href="#quickstart" style="color: #0366d6;">Quickstart</a> &nbsp;&nbsp;|&nbsp;&nbsp;
21
- <a href="#tui-interface" style="color: #0366d6;">TUI Interface</a> &nbsp;&nbsp;|&nbsp;&nbsp;
22
- <a href="#docker-deployment" style="color: #0366d6;">Docker Deployment</a> &nbsp;&nbsp;|&nbsp;&nbsp;
21
+ <a href="#install-python-package" style="color: #0366d6;">Python package</a> &nbsp;&nbsp;|&nbsp;&nbsp;
22
+ <a href="#docker-or-podman-installation" style="color: #0366d6;">Docker or Podman</a> &nbsp;&nbsp;|&nbsp;&nbsp;
23
23
  <a href="#development" style="color: #0366d6;">Development</a> &nbsp;&nbsp;|&nbsp;&nbsp;
24
24
  <a href="#troubleshooting" style="color: #0366d6;">Troubleshooting</a>
25
25
  </div>
26
26
 
27
27
  ## Quickstart
28
28
 
29
- To quickly run OpenRAG without creating or modifying any project files, use `uvx`:
29
+ To run OpenRAG without creating or modifying any project files, use `uvx`:
30
30
 
31
31
  ```bash
32
32
  uvx openrag
33
33
  ```
34
- This runs OpenRAG without installing it to your project or globally.
35
- To run a specific version of OpenRAG, add the version to the command, such as: `uvx --from openrag==0.1.25 openrag`.
34
+
35
+ This command runs OpenRAG without installing it to your project or globally.
36
+
37
+ To run a specific version of OpenRAG, run `uvx --from openrag==VERSION openrag`.
36
38
 
37
39
  ## Install Python package
38
40
 
39
- To first set up a project and then install the OpenRAG Python package, do the following:
41
+ To add the OpenRAG Python package to a Python project, use `uv`:
40
42
 
41
- 1. Create a new project with a virtual environment using `uv init`.
43
+ 1. Create a new project with a virtual environment using `uv init`:
42
44
 
43
45
  ```bash
44
46
  uv init YOUR_PROJECT_NAME
@@ -48,33 +50,33 @@ To first set up a project and then install the OpenRAG Python package, do the fo
48
50
  The `(venv)` prompt doesn't change, but `uv` commands will automatically use the project's virtual environment.
49
51
  For more information on virtual environments, see the [uv documentation](https://docs.astral.sh/uv/pip/environments).
50
52
 
51
- 2. Add OpenRAG to your project.
53
+ 2. Add OpenRAG to your project:
54
+
52
55
  ```bash
53
56
  uv add openrag
54
57
  ```
55
58
 
56
- To add a specific version of OpenRAG:
57
- ```bash
58
- uv add openrag==0.1.25
59
- ```
59
+ To add a specific version of OpenRAG, run `uv add openrag==VERSION`.
60
+
61
+ 3. Start the OpenRAG terminal user interface (TUI):
60
62
 
61
- 3. Start the OpenRAG TUI.
62
63
  ```bash
63
64
  uv run openrag
64
65
  ```
65
66
 
66
67
  4. Continue with the [Quickstart](https://docs.openr.ag/quickstart).
67
68
 
68
- For the full TUI installation guide, see [TUI](https://docs.openr.ag/install).
69
+ For all installation options, see the [OpenRAG installation guide](https://docs.openr.ag/install).
69
70
 
70
71
  ## Docker or Podman installation
71
72
 
72
- For more information, see [Install OpenRAG containers](https://docs.openr.ag/get-started/docker).
73
+ By default, OpenRAG automatically starts the required containers and helps you manage them.
74
+ To install OpenRAG with self-managed containers, see the [OpenRAG installation guide](https://docs.openr.ag/docker).
73
75
 
74
- ## Troubleshooting
76
+ ## Development
75
77
 
76
- For common issues and fixes, see [Troubleshoot](https://docs.openr.ag/support/troubleshoot).
78
+ For developers wanting to contribute to OpenRAG or set up a development environment, see [CONTRIBUTING.md](CONTRIBUTING.md).
77
79
 
78
- ## Development
80
+ ## Troubleshooting
79
81
 
80
- For developers wanting to contribute to OpenRAG or set up a development environment, see [CONTRIBUTING.md](CONTRIBUTING.md).
82
+ For common issues and fixes, see [Troubleshoot OpenRAG](https://docs.openr.ag/support/troubleshoot).
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "openrag"
7
- version = "0.1.31"
7
+ version = "0.2.0"
8
8
  description = "Add your description here"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -34,7 +34,8 @@ dependencies = [
34
34
  "structlog>=25.4.0",
35
35
  "docling-serve==1.5.0",
36
36
  "docling-core==2.48.1",
37
- "easyocr>=1.7.1"
37
+ "easyocr>=1.7.1; sys_platform != 'darwin'",
38
+ "zxcvbn>=4.5.0"
38
39
  ]
39
40
 
40
41
  [dependency-groups]
@@ -1,3 +1,5 @@
1
+ from http.client import HTTPException
2
+
1
3
  from utils.logging_config import get_logger
2
4
 
3
5
  logger = get_logger(__name__)
@@ -34,7 +36,7 @@ def get_conversation_thread(user_id: str, previous_response_id: str = None):
34
36
  "messages": [
35
37
  {
36
38
  "role": "system",
37
- "content": "You are a helpful assistant that can use tools to answer questions and perform tasks. You are part of OpenRAG, an assistant that analyzes documents and provides informations about them. When asked about what is OpenRAG, answer the following:\n\n\"OpenRAG is an open-source package for building agentic RAG systems. It supports integration with a wide range of orchestration tools, vector databases, and LLM providers. OpenRAG connects and amplifies three popular, proven open-source projects into one powerful platform:\n\n**Langflow** – Langflow is a powerful tool to build and deploy AI agents and MCP servers [Read more](https://www.langflow.org/)\n\n**OpenSearch** – Langflow is a powerful tool to build and deploy AI agents and MCP servers [Read more](https://opensearch.org/)\n\n**Docling** – Langflow is a powerful tool to build and deploy AI agents and MCP servers [Read more](https://www.docling.ai/)\"",
39
+ "content": "You are the OpenRAG Agent. You answer questions using retrieval, reasoning, and tool use.\nYou have access to several tools. Your job is to determine **which tool to use and when**.\n### Available Tools\n- OpenSearch Retrieval Tool:\n Use this to search the indexed knowledge base. Use when the user asks about product details, internal concepts, processes, architecture, documentation, roadmaps, or anything that may be stored in the index.\n- Conversation History:\n Use this to maintain continuity when the user is referring to previous turns. \n Do not treat history as a factual source.\n- Conversation File Context:\n Use this when the user asks about a document they uploaded or refers directly to its contents.\n **IMPORTANT**: If you receive confirmation that a file was uploaded (e.g., \"Confirm that you received this file\"), the file content is already available in the conversation context. Do NOT attempt to ingest it as a URL.\n Simply acknowledge the file and answer questions about it directly from the context.\n- URL Ingestion Tool:\n Use this **only** when the user explicitly asks you to read, summarize, or analyze the content of a web URL (http:// or https://).\n **Do NOT use this tool for filenames** (e.g., README.md, document.pdf, data.txt). These are file uploads, not URLs.\n Only use this tool for actual web addresses that the user explicitly provides.\n If unclear → ask a clarifying question.\n- Calculator / Expression Evaluation Tool:\n Use this when the user asks to compare numbers, compute estimates, calculate totals, analyze pricing, or answer any question requiring mathematics or quantitative reasoning.\n If the answer requires arithmetic, call the calculator tool rather than calculating internally.\n### Retrieval Decision Rules\nUse OpenSearch **whenever**:\n1. The question may be answered from internal or indexed data.\n2. The user references team names, product names, release plans, configurations, requirements, or official information.\n3. The user needs a factual, grounded answer.\nDo **not** use retrieval if:\n- The question is purely creative (e.g., storytelling, analogies) or personal preference.\n- The user simply wants text reformatted or rewritten from what is already present in the conversation.\nWhen uncertain → **Retrieve.** Retrieval is low risk and improves grounding.\n### File Upload vs URL Distinction\n**File uploads** (already in context):\n- Filenames like: README.md, document.pdf, notes.txt, data.csv\n- When you see file confirmation messages\n- Use conversation context directly - do NOT call URL tool\n**Web URLs** (need ingestion):\n- Start with http:// or https://\n- Examples: https://example.com, http://docs.site.org\n- User explicitly asks to fetch from web\n### Calculator Usage Rules\nUse the calculator when:\n- Performing arithmetic\n- Estimating totals\n- Comparing values\n- Modeling cost, time, effort, scale, or projections\nDo not perform math internally. **Call the calculator tool instead.**\n### Answer Construction Rules\n1. When asked: \"What is OpenRAG\", answer the following:\n\"OpenRAG is an open-source package for building agentic RAG systems. It supports integration with a wide range of orchestration tools, vector databases, and LLM providers. OpenRAG connects and amplifies three popular, proven open-source projects into one powerful platform:\n**Langflow** – Langflow is a powerful tool to build and deploy AI agents and MCP servers. [Read more](https://www.langflow.org/)\n**OpenSearch** – OpenSearch is an open source, search and observability suite that brings order to unstructured data at scale. [Read more](https://opensearch.org/)\n**Docling** – Docling simplifies document processing with advanced PDF understanding, OCR support, and seamless AI integrations. Parse PDFs, DOCX, PPTX, images & more. [Read more](https://www.docling.ai/)\"\n2. Synthesize retrieved or ingested content in your own words.\n3. Support factual claims with citations in the format:\n (Source: <document_name_or_id>)\n4. If no supporting evidence is found:\n Say: \"No relevant supporting sources were found for that request.\"\n5. Never invent facts or hallucinate details.\n6. Be concise, direct, and confident. \n7. Do not reveal internal chain-of-thought.",
38
40
  }
39
41
  ],
40
42
  "previous_response_id": previous_response_id, # Parent response_id for branching
@@ -45,8 +47,8 @@ def get_conversation_thread(user_id: str, previous_response_id: str = None):
45
47
  return new_conversation
46
48
 
47
49
 
48
- def store_conversation_thread(user_id: str, response_id: str, conversation_state: dict):
49
- """Store conversation both in memory (with function calls) and persist metadata to disk"""
50
+ async def store_conversation_thread(user_id: str, response_id: str, conversation_state: dict):
51
+ """Store conversation both in memory (with function calls) and persist metadata to disk (async, non-blocking)"""
50
52
  # 1. Store full conversation in memory for function call preservation
51
53
  if user_id not in active_conversations:
52
54
  active_conversations[user_id] = {}
@@ -67,13 +69,14 @@ def store_conversation_thread(user_id: str, response_id: str, conversation_state
67
69
  "created_at": conversation_state.get("created_at"),
68
70
  "last_activity": conversation_state.get("last_activity"),
69
71
  "previous_response_id": conversation_state.get("previous_response_id"),
72
+ "filter_id": conversation_state.get("filter_id"),
70
73
  "total_messages": len(
71
74
  [msg for msg in messages if msg.get("role") in ["user", "assistant"]]
72
75
  ),
73
76
  # Don't store actual messages - Langflow has them
74
77
  }
75
78
 
76
- conversation_persistence.store_conversation_thread(
79
+ await conversation_persistence.store_conversation_thread(
77
80
  user_id, response_id, metadata_only
78
81
  )
79
82
 
@@ -132,6 +135,7 @@ async def async_response_stream(
132
135
 
133
136
  full_response = ""
134
137
  chunk_count = 0
138
+ detected_tool_call = False # Track if we've detected a tool call
135
139
  async for chunk in response:
136
140
  chunk_count += 1
137
141
  logger.debug(
@@ -155,6 +159,17 @@ async def async_response_stream(
155
159
  else:
156
160
  delta_text = str(chunk.delta)
157
161
  full_response += delta_text
162
+
163
+ # Enhanced logging for tool call detection (Granite 3.3 8b investigation)
164
+ chunk_attrs = dir(chunk) if hasattr(chunk, '__dict__') else []
165
+ tool_related_attrs = [attr for attr in chunk_attrs if 'tool' in attr.lower() or 'call' in attr.lower() or 'retrieval' in attr.lower()]
166
+ if tool_related_attrs:
167
+ logger.info(
168
+ "Tool-related attributes found in chunk",
169
+ chunk_count=chunk_count,
170
+ attributes=tool_related_attrs,
171
+ chunk_type=type(chunk).__name__
172
+ )
158
173
 
159
174
  # Send the raw event as JSON followed by newline for easy parsing
160
175
  try:
@@ -166,7 +181,57 @@ async def async_response_stream(
166
181
  chunk_data = chunk.__dict__
167
182
  else:
168
183
  chunk_data = str(chunk)
169
-
184
+
185
+ # Log detailed chunk structure for investigation (especially for Granite 3.3 8b)
186
+ if isinstance(chunk_data, dict):
187
+ # Check for any fields that might indicate tool usage
188
+ potential_tool_fields = {
189
+ k: v for k, v in chunk_data.items()
190
+ if any(keyword in str(k).lower() for keyword in ['tool', 'call', 'retrieval', 'function', 'result', 'output'])
191
+ }
192
+ if potential_tool_fields:
193
+ logger.info(
194
+ "Potential tool-related fields in chunk",
195
+ chunk_count=chunk_count,
196
+ fields=list(potential_tool_fields.keys()),
197
+ sample_data=str(potential_tool_fields)[:500]
198
+ )
199
+
200
+ # Middleware: Detect implicit tool calls and inject standardized events
201
+ # This helps Granite 3.3 8b and other models that don't emit standard markers
202
+ if isinstance(chunk_data, dict) and not detected_tool_call:
203
+ # Check if this chunk contains retrieval results
204
+ has_results = any([
205
+ 'results' in chunk_data and isinstance(chunk_data.get('results'), list),
206
+ 'outputs' in chunk_data and isinstance(chunk_data.get('outputs'), list),
207
+ 'retrieved_documents' in chunk_data,
208
+ 'retrieval_results' in chunk_data,
209
+ ])
210
+
211
+ if has_results:
212
+ logger.info(
213
+ "Detected implicit tool call in backend, injecting synthetic event",
214
+ chunk_fields=list(chunk_data.keys())
215
+ )
216
+ # Inject a synthetic tool call event before this chunk
217
+ synthetic_event = {
218
+ "type": "response.output_item.done",
219
+ "item": {
220
+ "type": "retrieval_call",
221
+ "id": f"synthetic_{chunk_count}",
222
+ "name": "Retrieval",
223
+ "tool_name": "Retrieval",
224
+ "status": "completed",
225
+ "inputs": {"implicit": True, "backend_detected": True},
226
+ "results": chunk_data.get('results') or chunk_data.get('outputs') or
227
+ chunk_data.get('retrieved_documents') or
228
+ chunk_data.get('retrieval_results') or []
229
+ }
230
+ }
231
+ # Send the synthetic event first
232
+ yield (json.dumps(synthetic_event, default=str) + "\n").encode("utf-8")
233
+ detected_tool_call = True # Mark that we've injected a tool call
234
+
170
235
  yield (json.dumps(chunk_data, default=str) + "\n").encode("utf-8")
171
236
  except Exception as e:
172
237
  # Fallback to string representation
@@ -219,15 +284,26 @@ async def async_response(
219
284
 
220
285
  response = await client.responses.create(**request_params)
221
286
 
222
- response_text = response.output_text
223
- logger.info("Response generated", log_prefix=log_prefix, response=response_text)
287
+ # Check if response has output_text using getattr to avoid issues with special objects
288
+ output_text = getattr(response, "output_text", None)
289
+ if output_text is not None:
290
+ response_text = output_text
291
+ logger.info("Response generated", log_prefix=log_prefix, response=response_text)
224
292
 
225
- # Extract and store response_id if available
226
- response_id = getattr(response, "id", None) or getattr(
227
- response, "response_id", None
228
- )
293
+ # Extract and store response_id if available
294
+ response_id = getattr(response, "id", None) or getattr(
295
+ response, "response_id", None
296
+ )
229
297
 
230
- return response_text, response_id, response
298
+ return response_text, response_id, response
299
+ else:
300
+ msg = "Nudge response missing output_text"
301
+ error = getattr(response, "error", None)
302
+ if error:
303
+ error_msg = getattr(error, "message", None)
304
+ if error_msg:
305
+ msg = error_msg
306
+ raise ValueError(msg)
231
307
  except Exception as e:
232
308
  logger.error("Exception in non-streaming response", error=str(e))
233
309
  import traceback
@@ -314,6 +390,7 @@ async def async_chat(
314
390
  user_id: str,
315
391
  model: str = "gpt-4.1-mini",
316
392
  previous_response_id: str = None,
393
+ filter_id: str = None,
317
394
  ):
318
395
  logger.debug(
319
396
  "async_chat called", user_id=user_id, previous_response_id=previous_response_id
@@ -334,6 +411,10 @@ async def async_chat(
334
411
  "Added user message", message_count=len(conversation_state["messages"])
335
412
  )
336
413
 
414
+ # Store filter_id in conversation state if provided
415
+ if filter_id:
416
+ conversation_state["filter_id"] = filter_id
417
+
337
418
  response_text, response_id, response_obj = await async_response(
338
419
  async_client,
339
420
  prompt,
@@ -363,7 +444,7 @@ async def async_chat(
363
444
  # Store the conversation thread with its response_id
364
445
  if response_id:
365
446
  conversation_state["last_activity"] = datetime.now()
366
- store_conversation_thread(user_id, response_id, conversation_state)
447
+ await store_conversation_thread(user_id, response_id, conversation_state)
367
448
  logger.debug(
368
449
  "Stored conversation thread", user_id=user_id, response_id=response_id
369
450
  )
@@ -389,6 +470,7 @@ async def async_chat_stream(
389
470
  user_id: str,
390
471
  model: str = "gpt-4.1-mini",
391
472
  previous_response_id: str = None,
473
+ filter_id: str = None,
392
474
  ):
393
475
  # Get the specific conversation thread (or create new one)
394
476
  conversation_state = get_conversation_thread(user_id, previous_response_id)
@@ -399,6 +481,10 @@ async def async_chat_stream(
399
481
  user_message = {"role": "user", "content": prompt, "timestamp": datetime.now()}
400
482
  conversation_state["messages"].append(user_message)
401
483
 
484
+ # Store filter_id in conversation state if provided
485
+ if filter_id:
486
+ conversation_state["filter_id"] = filter_id
487
+
402
488
  full_response = ""
403
489
  response_id = None
404
490
  async for chunk in async_stream(
@@ -437,7 +523,7 @@ async def async_chat_stream(
437
523
  # Store the conversation thread with its response_id
438
524
  if response_id:
439
525
  conversation_state["last_activity"] = datetime.now()
440
- store_conversation_thread(user_id, response_id, conversation_state)
526
+ await store_conversation_thread(user_id, response_id, conversation_state)
441
527
  logger.debug(
442
528
  f"Stored conversation thread for user {user_id} with response_id: {response_id}"
443
529
  )
@@ -452,6 +538,7 @@ async def async_langflow_chat(
452
538
  extra_headers: dict = None,
453
539
  previous_response_id: str = None,
454
540
  store_conversation: bool = True,
541
+ filter_id: str = None,
455
542
  ):
456
543
  logger.debug(
457
544
  "async_langflow_chat called",
@@ -478,6 +565,10 @@ async def async_langflow_chat(
478
565
  message_count=len(conversation_state["messages"]),
479
566
  )
480
567
 
568
+ # Store filter_id in conversation state if provided
569
+ if filter_id:
570
+ conversation_state["filter_id"] = filter_id
571
+
481
572
  response_text, response_id, response_obj = await async_response(
482
573
  langflow_client,
483
574
  prompt,
@@ -520,7 +611,7 @@ async def async_langflow_chat(
520
611
  # Store the conversation thread with its response_id
521
612
  if response_id:
522
613
  conversation_state["last_activity"] = datetime.now()
523
- store_conversation_thread(user_id, response_id, conversation_state)
614
+ await store_conversation_thread(user_id, response_id, conversation_state)
524
615
 
525
616
  # Claim session ownership for this user
526
617
  try:
@@ -562,6 +653,7 @@ async def async_langflow_chat_stream(
562
653
  user_id: str,
563
654
  extra_headers: dict = None,
564
655
  previous_response_id: str = None,
656
+ filter_id: str = None,
565
657
  ):
566
658
  logger.debug(
567
659
  "async_langflow_chat_stream called",
@@ -578,6 +670,10 @@ async def async_langflow_chat_stream(
578
670
  user_message = {"role": "user", "content": prompt, "timestamp": datetime.now()}
579
671
  conversation_state["messages"].append(user_message)
580
672
 
673
+ # Store filter_id in conversation state if provided
674
+ if filter_id:
675
+ conversation_state["filter_id"] = filter_id
676
+
581
677
  full_response = ""
582
678
  response_id = None
583
679
  collected_chunks = [] # Store all chunks for function call data
@@ -622,7 +718,7 @@ async def async_langflow_chat_stream(
622
718
  # Store the conversation thread with its response_id
623
719
  if response_id:
624
720
  conversation_state["last_activity"] = datetime.now()
625
- store_conversation_thread(user_id, response_id, conversation_state)
721
+ await store_conversation_thread(user_id, response_id, conversation_state)
626
722
 
627
723
  # Claim session ownership for this user
628
724
  try:
@@ -638,8 +734,8 @@ async def async_langflow_chat_stream(
638
734
  )
639
735
 
640
736
 
641
- def delete_user_conversation(user_id: str, response_id: str) -> bool:
642
- """Delete a conversation for a user from both memory and persistent storage"""
737
+ async def delete_user_conversation(user_id: str, response_id: str) -> bool:
738
+ """Delete a conversation for a user from both memory and persistent storage (async, non-blocking)"""
643
739
  deleted = False
644
740
 
645
741
  try:
@@ -650,7 +746,7 @@ def delete_user_conversation(user_id: str, response_id: str) -> bool:
650
746
  deleted = True
651
747
 
652
748
  # Delete from persistent storage
653
- conversation_deleted = conversation_persistence.delete_conversation_thread(user_id, response_id)
749
+ conversation_deleted = await conversation_persistence.delete_conversation_thread(user_id, response_id)
654
750
  if conversation_deleted:
655
751
  logger.debug(f"Deleted conversation {response_id} from persistent storage for user {user_id}")
656
752
  deleted = True
@@ -1,5 +1,6 @@
1
1
  from starlette.requests import Request
2
2
  from starlette.responses import JSONResponse
3
+ from utils.telemetry import TelemetryClient, Category, MessageId
3
4
 
4
5
 
5
6
  async def auth_init(request: Request, auth_service, session_manager):
@@ -40,8 +41,11 @@ async def auth_callback(request: Request, auth_service, session_manager):
40
41
  connection_id, authorization_code, state, request
41
42
  )
42
43
 
44
+ await TelemetryClient.send_event(Category.AUTHENTICATION, MessageId.ORB_AUTH_OAUTH_CALLBACK)
45
+
43
46
  # If this is app auth, set JWT cookie
44
47
  if result.get("purpose") == "app_auth" and result.get("jwt_token"):
48
+ await TelemetryClient.send_event(Category.AUTHENTICATION, MessageId.ORB_AUTH_SUCCESS)
45
49
  response = JSONResponse(
46
50
  {k: v for k, v in result.items() if k != "jwt_token"}
47
51
  )
@@ -61,6 +65,7 @@ async def auth_callback(request: Request, auth_service, session_manager):
61
65
  import traceback
62
66
 
63
67
  traceback.print_exc()
68
+ await TelemetryClient.send_event(Category.AUTHENTICATION, MessageId.ORB_AUTH_OAUTH_FAILED)
64
69
  return JSONResponse({"error": f"Callback failed: {str(e)}"}, status_code=500)
65
70
 
66
71
 
@@ -72,6 +77,7 @@ async def auth_me(request: Request, auth_service, session_manager):
72
77
 
73
78
  async def auth_logout(request: Request, auth_service, session_manager):
74
79
  """Logout user by clearing auth cookie"""
80
+ await TelemetryClient.send_event(Category.AUTHENTICATION, MessageId.ORB_AUTH_LOGOUT)
75
81
  response = JSONResponse(
76
82
  {"status": "logged_out", "message": "Successfully logged out"}
77
83
  )
@@ -14,6 +14,7 @@ async def chat_endpoint(request: Request, chat_service, session_manager):
14
14
  filters = data.get("filters")
15
15
  limit = data.get("limit", 10)
16
16
  score_threshold = data.get("scoreThreshold", 0)
17
+ filter_id = data.get("filter_id")
17
18
 
18
19
  user = request.state.user
19
20
  user_id = user.user_id
@@ -42,6 +43,7 @@ async def chat_endpoint(request: Request, chat_service, session_manager):
42
43
  jwt_token,
43
44
  previous_response_id=previous_response_id,
44
45
  stream=True,
46
+ filter_id=filter_id,
45
47
  ),
46
48
  media_type="text/event-stream",
47
49
  headers={
@@ -58,6 +60,7 @@ async def chat_endpoint(request: Request, chat_service, session_manager):
58
60
  jwt_token,
59
61
  previous_response_id=previous_response_id,
60
62
  stream=False,
63
+ filter_id=filter_id,
61
64
  )
62
65
  return JSONResponse(result)
63
66
 
@@ -71,6 +74,7 @@ async def langflow_endpoint(request: Request, chat_service, session_manager):
71
74
  filters = data.get("filters")
72
75
  limit = data.get("limit", 10)
73
76
  score_threshold = data.get("scoreThreshold", 0)
77
+ filter_id = data.get("filter_id")
74
78
 
75
79
  user = request.state.user
76
80
  user_id = user.user_id
@@ -100,6 +104,7 @@ async def langflow_endpoint(request: Request, chat_service, session_manager):
100
104
  jwt_token,
101
105
  previous_response_id=previous_response_id,
102
106
  stream=True,
107
+ filter_id=filter_id,
103
108
  ),
104
109
  media_type="text/event-stream",
105
110
  headers={
@@ -116,6 +121,7 @@ async def langflow_endpoint(request: Request, chat_service, session_manager):
116
121
  jwt_token,
117
122
  previous_response_id=previous_response_id,
118
123
  stream=False,
124
+ filter_id=filter_id,
119
125
  )
120
126
  return JSONResponse(result)
121
127
 
@@ -1,6 +1,7 @@
1
1
  from starlette.requests import Request
2
2
  from starlette.responses import JSONResponse, PlainTextResponse
3
3
  from utils.logging_config import get_logger
4
+ from utils.telemetry import TelemetryClient, Category, MessageId
4
5
 
5
6
  logger = get_logger(__name__)
6
7
 
@@ -25,6 +26,7 @@ async def connector_sync(request: Request, connector_service, session_manager):
25
26
  selected_files = data.get("selected_files")
26
27
 
27
28
  try:
29
+ await TelemetryClient.send_event(Category.CONNECTOR_OPERATIONS, MessageId.ORB_CONN_SYNC_START)
28
30
  logger.debug(
29
31
  "Starting connector sync",
30
32
  connector_type=connector_type,
@@ -102,6 +104,7 @@ async def connector_sync(request: Request, connector_service, session_manager):
102
104
  jwt_token=jwt_token,
103
105
  )
104
106
  task_ids = [task_id]
107
+ await TelemetryClient.send_event(Category.CONNECTOR_OPERATIONS, MessageId.ORB_CONN_SYNC_COMPLETE)
105
108
  return JSONResponse(
106
109
  {
107
110
  "task_ids": task_ids,
@@ -114,6 +117,7 @@ async def connector_sync(request: Request, connector_service, session_manager):
114
117
 
115
118
  except Exception as e:
116
119
  logger.error("Connector sync failed", error=str(e))
120
+ await TelemetryClient.send_event(Category.CONNECTOR_OPERATIONS, MessageId.ORB_CONN_SYNC_FAILED)
117
121
  return JSONResponse({"error": f"Sync failed: {str(e)}"}, status_code=500)
118
122
 
119
123
 
@@ -185,6 +189,7 @@ async def connector_webhook(request: Request, connector_service, session_manager
185
189
  config=temp_config,
186
190
  )
187
191
  try:
192
+ await TelemetryClient.send_event(Category.CONNECTOR_OPERATIONS, MessageId.ORB_CONN_WEBHOOK_RECV)
188
193
  temp_connector = connector_service.connection_manager._create_connector(
189
194
  temp_connection
190
195
  )
@@ -336,6 +341,7 @@ async def connector_webhook(request: Request, connector_service, session_manager
336
341
 
337
342
  except Exception as e:
338
343
  logger.error("Webhook processing failed", error=str(e))
344
+ await TelemetryClient.send_event(Category.CONNECTOR_OPERATIONS, MessageId.ORB_CONN_WEBHOOK_FAILED)
339
345
  return JSONResponse(
340
346
  {"error": f"Webhook processing failed: {str(e)}"}, status_code=500
341
347
  )