diff --git a/pyproject.toml b/pyproject.toml index 2218c5a47..b64b031f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,11 +1,11 @@ [project] name = "uipath-langchain" -version = "0.11.14" +version = "0.11.15" description = "Python SDK that enables developers to build and deploy LangGraph agents to the UiPath Cloud Platform" readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.11" dependencies = [ - "uipath>=2.10.74, <2.11.0", + "uipath>=2.10.79, <2.11.0", "uipath-core>=0.5.17, <0.6.0", "uipath-platform>=0.1.61, <0.2.0", "uipath-runtime>=0.11.0, <0.12.0", diff --git a/src/uipath_langchain/agent/tools/escalation_tool.py b/src/uipath_langchain/agent/tools/escalation_tool.py index 72aebeece..45a167ee0 100644 --- a/src/uipath_langchain/agent/tools/escalation_tool.py +++ b/src/uipath_langchain/agent/tools/escalation_tool.py @@ -14,9 +14,11 @@ AgentEscalationRecipient, AgentEscalationRecipientType, AgentEscalationResourceConfig, + AgentQuickFormEscalationChannel, ArgumentEmailRecipient, ArgumentGroupNameRecipient, AssetRecipient, + EscalationChannel, LowCodeAgentDefinition, StandardRecipient, ) @@ -37,7 +39,12 @@ StructuredToolWithArgumentProperties, ) -from ..exceptions import AgentRuntimeError, AgentRuntimeErrorCode +from ..exceptions import ( + AgentRuntimeError, + AgentRuntimeErrorCode, + AgentStartupError, + AgentStartupErrorCode, +) from ..react.types import AgentGraphState from .escalation_memory import ( EscalationMemorySettings, @@ -250,14 +257,79 @@ def _get_exported_trace_id(trace_id: str | None) -> str | None: return trace_id +def _try_get_channel_app_name(channel: EscalationChannel) -> str | None: + return ( + channel.properties.app_name + if isinstance(channel, AgentEscalationChannel) + else None + ) + + +async def create_task_for_channel( + client: UiPath, + channel: EscalationChannel, + *, + title: str, + data: dict[str, Any], + recipient: TaskRecipient | None, + folder_path: str | None, +) -> Task: + """Create the human task backing an escalation channel.""" + if isinstance(channel, AgentQuickFormEscalationChannel): + schema_id = channel.properties.schema_id + assert schema_id is not None + return await client.tasks.create_quickform_async( + title=title, + task_schema_key=schema_id, + schema=channel.properties.form_schema, + data=data, + folder_path=folder_path, + recipient=recipient, + priority=channel.priority, + labels=channel.labels, + is_actionable_message_enabled=channel.properties.is_actionable_message_enabled, + actionable_message_metadata=channel.properties.actionable_message_meta_data, + ) + return await client.tasks.create_async( + title=title, + data=data, + app_name=channel.properties.app_name, + app_folder_path=folder_path, + recipient=recipient, + priority=channel.priority, + labels=channel.labels, + is_actionable_message_enabled=channel.properties.is_actionable_message_enabled, + actionable_message_metadata=channel.properties.actionable_message_meta_data, + ) + + +def _resolve_channel(resource: AgentEscalationResourceConfig) -> EscalationChannel: + """Return the escalation's channel, validating quick-form configuration.""" + channel = resource.channels[0] + if ( + isinstance(channel, AgentQuickFormEscalationChannel) + and channel.properties.schema_id is None + ): + raise AgentStartupError( + code=AgentStartupErrorCode.INVALID_TOOL_CONFIG, + title="Quick form escalation is missing a schema id", + detail=( + f"Escalation '{channel.name}' has a quick form " + "schema without a schemaId." + ), + category=UiPathErrorCategory.USER, + ) + return channel + + def create_escalation_tool( resource: AgentEscalationResourceConfig, agent: LowCodeAgentDefinition | None = None, ) -> StructuredTool: - """Uses interrupt() for Action Center human-in-the-loop.""" + """Build the human-in-the-loop escalation tool for an escalation resource.""" tool_name: str = f"escalate_{sanitize_tool_name(resource.name)}" - channel: AgentEscalationChannel = resource.channels[0] + channel: EscalationChannel = _resolve_channel(resource) input_model: Any = create_model(channel.input_schema) output_model: Any = create_model(channel.output_schema) @@ -327,16 +399,13 @@ async def escalate(**_tool_kwargs: Any): @durable_interrupt async def create_escalation_task(): client = UiPath() - created_task = await client.tasks.create_async( + created_task = await create_task_for_channel( + client, + channel, title=task_title, data=serialized_data, - app_name=channel.properties.app_name, - app_folder_path=folder_path, recipient=recipient, - priority=channel.priority, - labels=channel.labels, - is_actionable_message_enabled=channel.properties.is_actionable_message_enabled, - actionable_message_metadata=channel.properties.actionable_message_meta_data, + folder_path=folder_path, ) if created_task.id is not None: @@ -345,7 +414,7 @@ async def create_escalation_task(): return WaitEscalation( action=created_task, app_folder_path=folder_path, - app_name=channel.properties.app_name, + app_name=_try_get_channel_app_name(channel), recipient=recipient, ) @@ -487,7 +556,7 @@ async def escalation_wrapper( argument_properties=channel.argument_properties, metadata={ "tool_type": "escalation", - "display_name": channel.properties.app_name, + "display_name": _try_get_channel_app_name(channel) or channel.name, "channel_type": channel.type, "recipient": None, "args_schema": input_model, diff --git a/src/uipath_langchain/agent/tools/ixp_escalation_tool.py b/src/uipath_langchain/agent/tools/ixp_escalation_tool.py index ceb5c5441..45abfb07c 100644 --- a/src/uipath_langchain/agent/tools/ixp_escalation_tool.py +++ b/src/uipath_langchain/agent/tools/ixp_escalation_tool.py @@ -6,7 +6,10 @@ from langchain_core.messages import ToolCall from langchain_core.tools import StructuredTool from pydantic import BaseModel -from uipath.agent.models.agent import AgentIxpVsEscalationResourceConfig +from uipath.agent.models.agent import ( + AgentEscalationChannel, + AgentIxpVsEscalationResourceConfig, +) from uipath.eval.mocks import mockable from uipath.platform import UiPath from uipath.platform.common import WaitDocumentExtractionValidation @@ -25,7 +28,12 @@ ToolWrapperReturnType, ) -from ..exceptions import AgentRuntimeError, AgentRuntimeErrorCode +from ..exceptions import ( + AgentRuntimeError, + AgentRuntimeErrorCode, + AgentStartupError, + AgentStartupErrorCode, +) from .structured_tool_with_output_type import StructuredToolWithOutputType from .utils import ( resolve_task_title, @@ -42,6 +50,24 @@ class EmptyInput(BaseModel): pass +def _resolve_action_center_channel( + resource: AgentIxpVsEscalationResourceConfig, +) -> AgentEscalationChannel: + """Return the VS escalation's Action Center channel, rejecting quick forms.""" + channel = resource.channels[0] + if not isinstance(channel, AgentEscalationChannel): + raise AgentStartupError( + code=AgentStartupErrorCode.INVALID_TOOL_CONFIG, + title="Unsupported VS escalation channel", + detail=( + f"VS escalation '{resource.name}' must use an Action Center channel " + f"but received channel type '{channel.type.value}'." + ), + category=UiPathErrorCategory.USER, + ) + return channel + + def create_ixp_escalation_tool( resource: AgentIxpVsEscalationResourceConfig, ) -> StructuredTool: @@ -51,7 +77,7 @@ def create_ixp_escalation_tool( storage_bucket_folder_path: str = ( resource.vs_escalation_properties.storage_bucket_folder_path ) - channel = resource.channels[0] + channel = _resolve_action_center_channel(resource) action_priority = ActionPriority.from_str(channel.priority) ixp_tool_name: str = resource.vs_escalation_properties.ixp_tool_id diff --git a/tests/agent/tools/test_escalation_tool.py b/tests/agent/tools/test_escalation_tool.py index 78b685c8c..522b936d8 100644 --- a/tests/agent/tools/test_escalation_tool.py +++ b/tests/agent/tools/test_escalation_tool.py @@ -10,6 +10,8 @@ AgentEscalationChannelProperties, AgentEscalationRecipientType, AgentEscalationResourceConfig, + AgentQuickFormChannelProperties, + AgentQuickFormEscalationChannel, AssetRecipient, StandardRecipient, ) @@ -261,6 +263,32 @@ def escalation_resource_no_recipient(self): ], ) + @pytest.fixture + def quick_form_escalation_resource(self): + """Create a quick-form escalation resource (channel has no app_name).""" + return AgentEscalationResourceConfig( + name="approval", + description="Request approval", + channels=[ + AgentQuickFormEscalationChannel( + name="Escalation", + type="actionCenterQuickForm", + description="Quick Form channel", + input_schema={"type": "object", "properties": {}}, + output_schema={"type": "object", "properties": {}}, + properties=AgentQuickFormChannelProperties( + form_schema={"schemaId": "schema-123", "fields": []}, + ), + recipients=[ + StandardRecipient( + type=AgentEscalationRecipientType.USER_EMAIL, + value="user@example.com", + ) + ], + ) + ], + ) + @pytest.mark.asyncio async def test_escalation_tool_has_metadata(self, escalation_resource): """Test that escalation tool has metadata dict.""" @@ -283,6 +311,15 @@ async def test_escalation_tool_metadata_has_display_name(self, escalation_resour assert tool.metadata is not None assert tool.metadata["display_name"] == "ApprovalApp" + @pytest.mark.asyncio + async def test_escalation_tool_metadata_display_name_falls_back_to_channel_name( + self, quick_form_escalation_resource + ): + """Quick-form channels have no app_name; display_name uses the channel name.""" + tool = create_escalation_tool(quick_form_escalation_resource) + assert tool.metadata is not None + assert tool.metadata["display_name"] == "Escalation" + @pytest.mark.asyncio async def test_escalation_tool_metadata_has_channel_type(self, escalation_resource): """Test that metadata contains channel_type for span attributes.""" @@ -1320,3 +1357,216 @@ def test_builds_trace_and_search_payloads(self): } assert attributes == {"arguments": serialized_input} assert "escalation-input" not in attributes + + +class TestQuickFormEscalation: + """QuickForm channel (actionCenterQuickForm) path through create_escalation_tool.""" + + @pytest.fixture + def quick_form_schema(self): + return { + "schemaId": "00000000-0000-0000-0000-000000000abc", + "fields": [{"name": "decision", "type": "string"}], + "outcomes": ["approve", "reject"], + } + + @pytest.fixture + def quick_form_channel_dict(self, quick_form_schema): + return { + "name": "quick_form_channel", + "type": "actionCenterQuickForm", + "description": "Quick-form channel", + "inputSchema": {"type": "object", "properties": {}}, + "outputSchema": {"type": "object", "properties": {}}, + "properties": { + "schema": quick_form_schema, + "isActionableMessageEnabled": False, + "actionableMessageMetaData": None, + }, + "recipients": [], + } + + @pytest.fixture + def quick_form_resource(self, quick_form_channel_dict): + return AgentEscalationResourceConfig( + name="quick_form_approval", + description="Request quick-form approval", + channels=[AgentQuickFormEscalationChannel(**quick_form_channel_dict)], + ) + + @pytest.mark.asyncio + @patch.dict(os.environ, {"UIPATH_FOLDER_PATH": "/Test/Folder"}) + @patch("uipath_langchain.agent.tools.escalation_tool.UiPath") + @patch("uipath_langchain._utils.durable_interrupt.decorator.interrupt") + async def test_dispatches_to_create_quickform_async( + self, + mock_interrupt, + mock_uipath_class, + quick_form_resource, + quick_form_schema, + ): + task = _make_mock_task(id=777, key="task-key-777") + mock_client = MagicMock() + mock_client.tasks.create_quickform_async = AsyncMock(return_value=task) + mock_client.tasks.create_async = AsyncMock(return_value=task) + mock_uipath_class.return_value = mock_client + + mock_result = MagicMock() + mock_result.action = "approve" + mock_result.data = {} + mock_result.is_deleted = False + mock_interrupt.return_value = mock_result + + tool = create_escalation_tool(quick_form_resource) + call = ToolCall(args={}, id="test-call", name=tool.name) + await tool.awrapper(tool, call, {}) # type: ignore[attr-defined] + + mock_client.tasks.create_quickform_async.assert_called_once() + mock_client.tasks.create_async.assert_not_called() + + kwargs = mock_client.tasks.create_quickform_async.call_args[1] + assert kwargs["task_schema_key"] == "00000000-0000-0000-0000-000000000abc" + assert kwargs["schema"] == quick_form_schema + assert kwargs["folder_path"] == "/Test/Folder" + assert "app_name" not in kwargs + assert "app_folder_path" not in kwargs + + @pytest.mark.asyncio + @patch("uipath_langchain.agent.tools.escalation_tool.UiPath") + @patch("uipath_langchain._utils.durable_interrupt.decorator.interrupt") + async def test_wait_escalation_app_name_is_none_for_quick_form( + self, mock_interrupt, mock_uipath_class, quick_form_resource + ): + from uipath.platform.common import WaitEscalation + + task = _make_mock_task(id=778) + mock_client = MagicMock() + mock_client.tasks.create_quickform_async = AsyncMock(return_value=task) + mock_uipath_class.return_value = mock_client + + mock_result = MagicMock() + mock_result.action = "approve" + mock_result.data = {} + mock_result.is_deleted = False + mock_interrupt.return_value = mock_result + + tool = create_escalation_tool(quick_form_resource) + call = ToolCall(args={}, id="test-call", name=tool.name) + await tool.awrapper(tool, call, {}) # type: ignore[attr-defined] + + mock_interrupt.assert_called_once() + interrupt_arg = mock_interrupt.call_args[0][0] + assert isinstance(interrupt_arg, WaitEscalation) + assert interrupt_arg.app_name is None + assert interrupt_arg.action == task + + @pytest.mark.asyncio + @patch("uipath_langchain.agent.tools.escalation_tool.UiPath") + @patch("uipath_langchain._utils.durable_interrupt.decorator.interrupt") + async def test_outcome_mapping_end_terminates_agent( + self, + mock_interrupt, + mock_uipath_class, + quick_form_channel_dict, + ): + from uipath_langchain.agent.exceptions import AgentRuntimeError + + channel = dict(quick_form_channel_dict) + channel["outcomeMapping"] = {"approve": "end", "reject": "continue"} + resource = AgentEscalationResourceConfig( + name="quick_form_approval", + description="Request quick-form approval", + channels=[AgentQuickFormEscalationChannel(**channel)], + ) + + task = _make_mock_task(id=779) + mock_client = MagicMock() + mock_client.tasks.create_quickform_async = AsyncMock(return_value=task) + mock_uipath_class.return_value = mock_client + + mock_result = MagicMock() + mock_result.action = "approve" + mock_result.data = {} + mock_result.is_deleted = False + mock_interrupt.return_value = mock_result + + tool = create_escalation_tool(resource) + call = ToolCall(args={}, id="test-call", name=tool.name) + + with pytest.raises(AgentRuntimeError): + await tool.awrapper(tool, call, {}) # type: ignore[attr-defined] + + @pytest.mark.asyncio + async def test_tool_metadata_for_quick_form_resource(self, quick_form_resource): + tool = create_escalation_tool(quick_form_resource) + assert tool.metadata is not None + assert tool.metadata["tool_type"] == "escalation" + assert tool.metadata["channel_type"] == "actionCenterQuickForm" + assert "_span_context" in tool.metadata + assert "_bts_context" in tool.metadata + + async def test_missing_schema_id_raises_on_construction( + self, quick_form_channel_dict + ): + from uipath_langchain.agent.exceptions import AgentStartupError + + channel = dict(quick_form_channel_dict) + channel["properties"] = { + "schema": {"fields": [], "outcomes": []}, + "isActionableMessageEnabled": False, + "actionableMessageMetaData": None, + } + resource = AgentEscalationResourceConfig( + name="quick_form_approval", + description="Request quick-form approval", + channels=[AgentQuickFormEscalationChannel(**channel)], + ) + + with pytest.raises(AgentStartupError) as exc_info: + create_escalation_tool(resource) + + assert "INVALID_TOOL_CONFIG" in exc_info.value.error_info.code + + @pytest.mark.asyncio + @patch.dict(os.environ, {"UIPATH_FOLDER_PATH": "/Test/Folder"}) + @patch("uipath_langchain.agent.tools.escalation_tool.UiPath") + @patch("uipath_langchain._utils.durable_interrupt.decorator.interrupt") + async def test_action_center_channel_does_not_dispatch_to_quickform( + self, mock_interrupt, mock_uipath_class + ): + resource = AgentEscalationResourceConfig( + name="action_center_approval", + description="Request approval", + channels=[ + AgentEscalationChannel( + name="action_center_channel", + type="actionCenter", + description="Action Center channel", + input_schema={"type": "object", "properties": {}}, + output_schema={"type": "object", "properties": {}}, + properties=AgentEscalationChannelProperties( + app_name="ApprovalApp", app_version=1 + ), + recipients=[], + ) + ], + ) + + task = _make_mock_task(id=780) + mock_client = MagicMock() + mock_client.tasks.create_async = AsyncMock(return_value=task) + mock_client.tasks.create_quickform_async = AsyncMock(return_value=task) + mock_uipath_class.return_value = mock_client + + mock_result = MagicMock() + mock_result.action = "approve" + mock_result.data = {} + mock_result.is_deleted = False + mock_interrupt.return_value = mock_result + + tool = create_escalation_tool(resource) + call = ToolCall(args={}, id="test-call", name=tool.name) + await tool.awrapper(tool, call, {}) # type: ignore[attr-defined] + + mock_client.tasks.create_async.assert_called_once() + mock_client.tasks.create_quickform_async.assert_not_called() diff --git a/tests/agent/tools/test_ixp_escalation_tool.py b/tests/agent/tools/test_ixp_escalation_tool.py index 51377991d..dd360b943 100644 --- a/tests/agent/tools/test_ixp_escalation_tool.py +++ b/tests/agent/tools/test_ixp_escalation_tool.py @@ -10,6 +10,8 @@ AgentEscalationChannel, AgentEscalationChannelProperties, AgentIxpVsEscalationResourceConfig, + AgentQuickFormChannelProperties, + AgentQuickFormEscalationChannel, ) from uipath.platform.common import WaitDocumentExtractionValidation from uipath.platform.documents import ( @@ -18,7 +20,7 @@ ValidateExtractionAction, ) -from uipath_langchain.agent.exceptions import AgentRuntimeError +from uipath_langchain.agent.exceptions import AgentRuntimeError, AgentStartupError from uipath_langchain.agent.react.types import AgentGraphState, InnerAgentGraphState from uipath_langchain.agent.tools.ixp_escalation_tool import create_ixp_escalation_tool @@ -131,6 +133,45 @@ def test_tool_has_vs_escalation_metadata(self, escalation_resource): assert tool.metadata["ixp_tool_id"] == "some_tool_id" assert tool.metadata["storage_bucket_name"] == "some_bucket_name" + def test_tool_metadata_display_name_is_app_name(self, escalation_resource): + """Test that display_name comes from the Action Center channel app name.""" + tool = create_ixp_escalation_tool(escalation_resource) + assert tool.metadata is not None + assert tool.metadata["display_name"] == "ApprovalApp" + + def test_create_raises_for_quick_form_channel(self): + """Test that a VS escalation rejects a quick-form channel at construction.""" + resource = AgentIxpVsEscalationResourceConfig( + name="validate_invoice", + description="Validate extracted invoice data", + input_schema={"type": "object", "properties": {}}, + output_schema={"type": "object", "properties": {}}, + vs_escalation_properties={ + "ixpToolId": "some_tool_id", + "storageBucketName": "some_bucket_name", + "storageBucketFolderPath": "some_solution_folder", + }, + channels=[ + AgentQuickFormEscalationChannel( + name="quick_form", + type="actionCenterQuickForm", + description="Quick Form channel", + input_schema={"type": "object", "properties": {}}, + output_schema={"type": "object", "properties": {}}, + properties=AgentQuickFormChannelProperties( + form_schema={"schemaId": "schema-123", "fields": []}, + ), + recipients=[], + ) + ], + ) + + with pytest.raises(AgentStartupError) as exc_info: + create_ixp_escalation_tool(resource) + + assert "INVALID_TOOL_CONFIG" in exc_info.value.error_info.code + assert "actionCenterQuickForm" in exc_info.value.error_info.detail + class TestIxpEscalationToolWrapper: """Test the wrapper functionality of ixp escalation tool.""" diff --git a/tests/agent/tools/test_tool_factory.py b/tests/agent/tools/test_tool_factory.py index bd074022a..051d495f6 100644 --- a/tests/agent/tools/test_tool_factory.py +++ b/tests/agent/tools/test_tool_factory.py @@ -23,6 +23,8 @@ AgentMcpTool, AgentProcessToolProperties, AgentProcessToolResourceConfig, + AgentQuickFormChannelProperties, + AgentQuickFormEscalationChannel, AgentResourceType, AgentSettings, AgentToolType, @@ -149,6 +151,34 @@ def escalation_resource() -> AgentEscalationResourceConfig: ) +@pytest.fixture +def quick_form_resource() -> AgentEscalationResourceConfig: + """Create a quick-form escalation tool resource config.""" + return AgentEscalationResourceConfig( + resource_type=AgentResourceType.ESCALATION, + name="test_quick_form_escalation", + description="Test quick-form escalation description", + channels=[ + AgentQuickFormEscalationChannel( + name="test_channel", + type="actionCenterQuickForm", + description="Test quick-form channel", + task_title="Test Task", + input_schema=EMPTY_SCHEMA, + output_schema=EMPTY_SCHEMA, + properties=AgentQuickFormChannelProperties( + form_schema={ + "schemaId": "00000000-0000-0000-0000-000000000abc", + "fields": [{"name": "decision", "type": "string"}], + "outcomes": ["approve", "reject"], + }, + ), + recipients=[], + ) + ], + ) + + @pytest.fixture def integration_resource() -> AgentIntegrationToolResourceConfig: """Create an integration tool resource config.""" @@ -306,6 +336,7 @@ async def test_only_enabled_tools_returned( "function_resource", "context_resource", "escalation_resource", + "quick_form_resource", "integration_resource", "internal_resource", "ixp_extraction_resource", @@ -339,6 +370,24 @@ async def test_flow_resource_routes_through_process_tool_path( mock_create_process_tool.assert_called_once_with(flow_resource, run_as_me=False) assert tool is not None + async def test_quick_form_resource_routes_through_escalation_tool_path( + self, quick_form_resource, mock_uipath_sdk + ): + """A QuickForm escalation resource is dispatched via create_escalation_tool.""" + mock_llm = AsyncMock(spec=BaseChatModel) + with patch( + "uipath_langchain.agent.tools.tool_factory.create_escalation_tool" + ) as mock_create_escalation_tool: + mock_create_escalation_tool.return_value = MagicMock( + spec=BaseUiPathStructuredTool + ) + tool = await _build_tool_for_resource(quick_form_resource, mock_llm) + + mock_create_escalation_tool.assert_called_once_with( + quick_form_resource, agent=None + ) + assert tool is not None + async def test_function_resource_routes_through_process_tool_path( self, function_resource, mock_uipath_sdk ): diff --git a/uv.lock b/uv.lock index 507745600..a43f8dec0 100644 --- a/uv.lock +++ b/uv.lock @@ -4344,7 +4344,7 @@ wheels = [ [[package]] name = "uipath" -version = "2.10.74" +version = "2.10.79" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "applicationinsights" }, @@ -4367,9 +4367,9 @@ dependencies = [ { name = "uipath-platform" }, { name = "uipath-runtime" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/26/55/8e00773946979f80934fd5ee0957f3e55b6a13a01b0de6b813a72caef001/uipath-2.10.74.tar.gz", hash = "sha256:6fa9d677559562c7ca0faef51660dfdc4803b744c233af6453d410b06c60ef1a", size = 2947956, upload-time = "2026-05-29T16:18:42.389Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/11/d9217e8be4a38414a41c0f03d269cb6fd0b68875a2da3c63c825fbf8ceee/uipath-2.10.79.tar.gz", hash = "sha256:6c5b9a7f55edf2e6ab7e2b09a676f9d4c76c602952470da02d3e2332e6b79b1c", size = 4434820, upload-time = "2026-06-09T06:49:52.867Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/01/40029c2447610348bbae1049f398ee194396d04b170af44c20a235869b36/uipath-2.10.74-py3-none-any.whl", hash = "sha256:fc8033cbe634bc0dabe2c9f8d2cee9f9ddef439aad207bcc2bdcbfb4f491388c", size = 391889, upload-time = "2026-05-29T16:18:40.047Z" }, + { url = "https://files.pythonhosted.org/packages/49/84/13fdecc2edb85d9a7148cb6adac518f7da74bc0febe357c4a2c33f14bf4c/uipath-2.10.79-py3-none-any.whl", hash = "sha256:e373ecf855769968c814fc17efba65b2c2aab6e5a24394bfe2698663f919cd7c", size = 393048, upload-time = "2026-06-09T06:49:50.897Z" }, ] [[package]] @@ -4388,7 +4388,7 @@ wheels = [ [[package]] name = "uipath-langchain" -version = "0.11.14" +version = "0.11.15" source = { editable = "." } dependencies = [ { name = "a2a-sdk" }, @@ -4463,7 +4463,7 @@ requires-dist = [ { name = "pillow", specifier = ">=12.1.1" }, { name = "pydantic-settings", specifier = ">=2.6.0" }, { name = "python-dotenv", specifier = ">=1.0.1" }, - { name = "uipath", specifier = ">=2.10.74,<2.11.0" }, + { name = "uipath", specifier = ">=2.10.79,<2.11.0" }, { name = "uipath-core", specifier = ">=0.5.17,<0.6.0" }, { name = "uipath-langchain-client", extras = ["all"], marker = "extra == 'all'", specifier = ">=1.13.0,<1.14.0" }, { name = "uipath-langchain-client", extras = ["anthropic"], marker = "extra == 'anthropic'", specifier = ">=1.13.0,<1.14.0" },