1616 DeepRagFileExtension ,
1717 DeepRagFileExtensionSetting ,
1818)
19+ from uipath .platform .context_grounding import DeepRagResponse , DeepRagStatus , IndexStatus
20+ from uipath .platform .context_grounding .context_grounding import DeepRagContent
1921from uipath .platform .context_grounding .context_grounding_index import (
2022 ContextGroundingIndex ,
2123)
@@ -152,8 +154,16 @@ async def test_create_deeprag_tool_static_query_index_ready(
152154
153155 # Index is ready → ReadyEphemeralIndex skips interrupt() (no scratchpad in tests).
154156 # Only create_deeprag calls interrupt().
157+ deeprag_id = str (uuid .uuid4 ())
155158 mock_interrupt .side_effect = [
156- {"text" : "Deep RAG analysis result" },
159+ DeepRagResponse (
160+ id = deeprag_id ,
161+ name = "test-deeprag" ,
162+ created_date = "2024-01-01" ,
163+ last_deep_rag_status = DeepRagStatus .SUCCESSFUL ,
164+ content = DeepRagContent (text = "Deep RAG analysis result" , citations = []),
165+ failure_reason = None ,
166+ ),
157167 ]
158168
159169 mock_wrapper = Mock ()
@@ -175,7 +185,7 @@ async def test_create_deeprag_tool_static_query_index_ready(
175185 result = await tool .coroutine (attachment = mock_attachment )
176186
177187 # Verify result
178- assert result == {"text" : "Deep RAG analysis result" }
188+ assert result == {"text" : "Deep RAG analysis result" , "citations" : [], "deepRagId" : deeprag_id }
179189
180190 # Verify ephemeral index was created
181191 mock_uipath .context_grounding .create_ephemeral_index_async .assert_called_once ()
@@ -228,9 +238,17 @@ async def test_create_deeprag_tool_static_query_wait_for_ingestion(
228238 )
229239
230240 # First interrupt returns completed index, second returns DeepRAG result
241+ deeprag_id = str (uuid .uuid4 ())
231242 mock_interrupt .side_effect = [
232243 mock_index_complete ,
233- {"text" : "Deep RAG analysis after waiting" },
244+ DeepRagResponse (
245+ id = deeprag_id ,
246+ name = "test-deeprag" ,
247+ created_date = "2024-01-01" ,
248+ last_deep_rag_status = DeepRagStatus .SUCCESSFUL ,
249+ content = DeepRagContent (text = "Deep RAG analysis after waiting" , citations = []),
250+ failure_reason = None ,
251+ ),
234252 ]
235253
236254 mock_wrapper = Mock ()
@@ -248,7 +266,7 @@ async def test_create_deeprag_tool_static_query_wait_for_ingestion(
248266 result = await tool .coroutine (attachment = mock_attachment )
249267
250268 # Verify result
251- assert result == {"text" : "Deep RAG analysis after waiting" }
269+ assert result == {"text" : "Deep RAG analysis after waiting" , "citations" : [], "deepRagId" : deeprag_id }
252270
253271 # Verify interrupt was called twice (WaitEphemeralIndex + CreateDeepRag)
254272 assert mock_interrupt .call_count == 2
@@ -286,8 +304,16 @@ async def test_create_deeprag_tool_dynamic_query(
286304 )
287305
288306 # Index is ready → ReadyEphemeralIndex skips interrupt(). Only create_deeprag fires.
307+ deeprag_id = str (uuid .uuid4 ())
289308 mock_interrupt .side_effect = [
290- {"content" : "Dynamic query result" },
309+ DeepRagResponse (
310+ id = deeprag_id ,
311+ name = "test-deeprag" ,
312+ created_date = "2024-01-01" ,
313+ last_deep_rag_status = DeepRagStatus .SUCCESSFUL ,
314+ content = DeepRagContent (text = "Dynamic query result" , citations = []),
315+ failure_reason = None ,
316+ ),
291317 ]
292318
293319 mock_wrapper = Mock ()
@@ -307,7 +333,7 @@ async def test_create_deeprag_tool_dynamic_query(
307333 )
308334
309335 # Verify result
310- assert result == {"content " : "Dynamic query result" }
336+ assert result == {"text " : "Dynamic query result" , "citations" : [], "deepRagId" : deeprag_id }
311337
312338 @patch (
313339 "uipath_langchain.agent.wrappers.job_attachment_wrapper.get_job_attachment_wrapper"
@@ -345,6 +371,162 @@ async def test_create_deeprag_tool_missing_query_dynamic(
345371 with pytest .raises (ValueError , match = "Query is required for DeepRAG tool" ):
346372 await tool .coroutine (attachment = mock_attachment )
347373
374+ @patch (
375+ "uipath_langchain.agent.wrappers.job_attachment_wrapper.get_job_attachment_wrapper"
376+ )
377+ @patch ("uipath_langchain.agent.tools.internal_tools.deeprag_tool.UiPath" )
378+ @patch ("uipath_langchain.agent.tools.durable_interrupt.decorator.interrupt" )
379+ @patch (
380+ "uipath_langchain.agent.tools.internal_tools.deeprag_tool.mockable" ,
381+ lambda ** kwargs : lambda f : f ,
382+ )
383+ async def test_invoke_returns_error_message_on_failed_deeprag (
384+ self ,
385+ mock_interrupt ,
386+ mock_uipath_class ,
387+ mock_get_wrapper ,
388+ resource_config_static ,
389+ mock_llm ,
390+ ):
391+ """Test that tool returns failure_reason string when DeepRAG processing fails."""
392+ mock_uipath = AsyncMock ()
393+ mock_uipath_class .return_value = mock_uipath
394+
395+ mock_index = ContextGroundingIndex (
396+ id = str (uuid .uuid4 ()),
397+ name = "ephemeral-index-123" ,
398+ last_ingestion_status = IndexStatus .SUCCESSFUL ,
399+ )
400+ mock_uipath .context_grounding .create_ephemeral_index_async = AsyncMock (
401+ return_value = mock_index
402+ )
403+
404+ failed_deep_rag = DeepRagResponse (
405+ id = str (uuid .uuid4 ()),
406+ name = "test-deeprag" ,
407+ created_date = "2024-01-01" ,
408+ last_deep_rag_status = DeepRagStatus .FAILED ,
409+ content = None ,
410+ failure_reason = "DeepRAG processing failed due to an internal error" ,
411+ )
412+
413+ # Index is ready (no interrupt for index), DeepRAG fails
414+ mock_interrupt .side_effect = [failed_deep_rag ]
415+
416+ mock_wrapper = Mock ()
417+ mock_get_wrapper .return_value = mock_wrapper
418+
419+ tool = create_deeprag_tool (resource_config_static , mock_llm )
420+
421+ mock_attachment = MockAttachment (
422+ ID = str (uuid .uuid4 ()), FullName = "test.pdf" , MimeType = "application/pdf"
423+ )
424+
425+ assert tool .coroutine is not None
426+ result = await tool .coroutine (attachment = mock_attachment )
427+
428+ assert result == "DeepRAG processing failed due to an internal error"
429+ assert mock_interrupt .call_count == 1
430+
431+ @patch (
432+ "uipath_langchain.agent.wrappers.job_attachment_wrapper.get_job_attachment_wrapper"
433+ )
434+ @patch ("uipath_langchain.agent.tools.internal_tools.deeprag_tool.UiPath" )
435+ @patch (
436+ "uipath_langchain.agent.tools.internal_tools.deeprag_tool.mockable" ,
437+ lambda ** kwargs : lambda f : f ,
438+ )
439+ async def test_invoke_returns_error_message_on_failed_ephemeral_index (
440+ self ,
441+ mock_uipath_class ,
442+ mock_get_wrapper ,
443+ resource_config_static ,
444+ mock_llm ,
445+ ):
446+ """Test that tool returns failure reason when ephemeral index fails immediately."""
447+ mock_uipath = AsyncMock ()
448+ mock_uipath_class .return_value = mock_uipath
449+
450+ mock_index = ContextGroundingIndex (
451+ id = str (uuid .uuid4 ()),
452+ name = "ephemeral-index-123" ,
453+ last_ingestion_status = IndexStatus .FAILED ,
454+ last_ingestion_failure_reason = "Ingestion failed due to unsupported file format" ,
455+ )
456+ mock_uipath .context_grounding .create_ephemeral_index_async = AsyncMock (
457+ return_value = mock_index
458+ )
459+
460+ mock_wrapper = Mock ()
461+ mock_get_wrapper .return_value = mock_wrapper
462+
463+ tool = create_deeprag_tool (resource_config_static , mock_llm )
464+
465+ mock_attachment = MockAttachment (
466+ ID = str (uuid .uuid4 ()), FullName = "test.pdf" , MimeType = "application/pdf"
467+ )
468+
469+ assert tool .coroutine is not None
470+ result = await tool .coroutine (attachment = mock_attachment )
471+
472+ assert result == "Ingestion failed due to unsupported file format"
473+
474+ @patch (
475+ "uipath_langchain.agent.wrappers.job_attachment_wrapper.get_job_attachment_wrapper"
476+ )
477+ @patch ("uipath_langchain.agent.tools.internal_tools.deeprag_tool.UiPath" )
478+ @patch ("uipath_langchain.agent.tools.durable_interrupt.decorator.interrupt" )
479+ @patch (
480+ "uipath_langchain.agent.tools.internal_tools.deeprag_tool.mockable" ,
481+ lambda ** kwargs : lambda f : f ,
482+ )
483+ async def test_invoke_returns_error_message_on_failed_ephemeral_index_after_wait (
484+ self ,
485+ mock_interrupt ,
486+ mock_uipath_class ,
487+ mock_get_wrapper ,
488+ resource_config_static ,
489+ mock_llm ,
490+ ):
491+ """Test that tool returns failure reason when ephemeral index fails after waiting."""
492+ mock_uipath = AsyncMock ()
493+ mock_uipath_class .return_value = mock_uipath
494+
495+ pending_id = str (uuid .uuid4 ())
496+ mock_index_pending = ContextGroundingIndex (
497+ id = pending_id ,
498+ name = "ephemeral-index-456" ,
499+ last_ingestion_status = IndexStatus .IN_PROGRESS ,
500+ )
501+ mock_uipath .context_grounding .create_ephemeral_index_async = AsyncMock (
502+ return_value = mock_index_pending
503+ )
504+
505+ mock_index_failed = {
506+ "id" : pending_id ,
507+ "name" : mock_index_pending .name ,
508+ "last_ingestion_status" : "Failed" ,
509+ "last_ingestion_failure_reason" : "Ingestion failed during processing" ,
510+ }
511+
512+ # First (and only) interrupt returns the failed index; DeepRAG is never reached
513+ mock_interrupt .side_effect = [mock_index_failed ]
514+
515+ mock_wrapper = Mock ()
516+ mock_get_wrapper .return_value = mock_wrapper
517+
518+ tool = create_deeprag_tool (resource_config_static , mock_llm )
519+
520+ mock_attachment = MockAttachment (
521+ ID = str (uuid .uuid4 ()), FullName = "test.pdf" , MimeType = "application/pdf"
522+ )
523+
524+ assert tool .coroutine is not None
525+ result = await tool .coroutine (attachment = mock_attachment )
526+
527+ assert result == "Ingestion failed during processing"
528+ assert mock_interrupt .call_count == 1
529+
348530 @patch (
349531 "uipath_langchain.agent.wrappers.job_attachment_wrapper.get_job_attachment_wrapper"
350532 )
0 commit comments