Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/uipath/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "uipath"
version = "2.14.4"
version = "2.14.5"
description = "Python SDK and CLI for UiPath Platform, enabling programmatic interaction with automation services, process management, and deployment tools."
readme = { file = "README.md", content-type = "text/markdown" }
requires-python = ">=3.11"
Expand Down
6 changes: 4 additions & 2 deletions packages/uipath/src/uipath/eval/mocks/_input_mocker.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from .._execution_context import eval_set_run_id_context
from ._mock_context import cache_manager_context
from ._mocker import UiPathInputMockingError
from ._mocker import UiPathInputMockingError, format_exception_message
from ._structured_output import generate_structured_output
from ._types import (
InputMockingStrategy,
Expand Down Expand Up @@ -155,4 +155,6 @@ async def generate_llm_input(
except UiPathInputMockingError:
raise
except Exception as e:
raise UiPathInputMockingError(f"Failed to generate input: {str(e)}") from e
raise UiPathInputMockingError(
f"Failed to generate input: {format_exception_message(e)}"
) from e
5 changes: 4 additions & 1 deletion packages/uipath/src/uipath/eval/mocks/_llm_mocker.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
T,
UiPathMockResponseGenerationError,
UiPathNoMockFoundError,
format_exception_message,
)
from ._structured_output import generate_structured_output
from ._types import (
Expand Down Expand Up @@ -230,7 +231,9 @@ async def response(

return result
except Exception as e:
raise UiPathMockResponseGenerationError(str(e)) from e
raise UiPathMockResponseGenerationError(
format_exception_message(e)
) from e
else:
raise UiPathNoMockFoundError(f"Method '{function_name}' is not simulated.")

Expand Down
11 changes: 11 additions & 0 deletions packages/uipath/src/uipath/eval/mocks/_mocker.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ async def response(
raise NotImplementedError()


def format_exception_message(e: BaseException) -> str:
"""Format an exception for wrapped error messages, always naming its type.

Timeout and cancellation exceptions (``asyncio.TimeoutError``,
``CancelledError``, httpx timeouts) often have an empty ``str``, which
otherwise produces blank, undiagnosable error messages downstream.
"""
message = str(e).strip()
return f"{type(e).__name__}: {message}" if message else type(e).__name__
Comment on lines +32 to +33


class UiPathNoMockFoundError(Exception):
"""Exception when a mocker is unable to find a match with the invocation. This is a signal to invoke the real function."""

Expand Down
51 changes: 51 additions & 0 deletions packages/uipath/tests/cli/eval/mocks/test_mock_error_messages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import asyncio
from typing import Any

import pytest
from _pytest.monkeypatch import MonkeyPatch

from uipath.eval.mocks import _input_mocker
from uipath.eval.mocks._input_mocker import generate_llm_input
from uipath.eval.mocks._mocker import (
UiPathInputMockingError,
format_exception_message,
)
from uipath.eval.mocks._types import InputMockingStrategy


def test_format_exception_message_with_message():
assert format_exception_message(ValueError("bad value")) == "ValueError: bad value"


def test_format_exception_message_empty_str():
# asyncio.TimeoutError and CancelledError stringify to "" — the type name
# must still be surfaced so the wrapped message is never blank.
assert format_exception_message(asyncio.TimeoutError()) == "TimeoutError"
assert format_exception_message(asyncio.CancelledError()) == "CancelledError"


def test_format_exception_message_whitespace_only():
assert format_exception_message(RuntimeError(" ")) == "RuntimeError"


@pytest.mark.asyncio
async def test_generate_llm_input_wraps_empty_str_exception(
monkeypatch: MonkeyPatch,
):
monkeypatch.setenv("UIPATH_URL", "https://example.com")
monkeypatch.setenv("UIPATH_ACCESS_TOKEN", "test-token")

async def raise_timeout(*args: Any, **kwargs: Any) -> Any:
raise asyncio.TimeoutError()

monkeypatch.setattr(_input_mocker, "generate_structured_output", raise_timeout)

with pytest.raises(UiPathInputMockingError) as exc_info:

Check warning on line 43 in packages/uipath/tests/cli/eval/mocks/test_mock_error_messages.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this exception test to have only one invocation possibly throwing an exception.

See more on https://sonarcloud.io/project/issues?id=UiPath_uipath-python&issues=AZ_9TyFgis02fnzkFPAM&open=AZ_9TyFgis02fnzkFPAM&pullRequest=1862
await generate_llm_input(
mocking_strategy=InputMockingStrategy(prompt="generate something"),
input_schema={"type": "object", "properties": {}},
expected_behavior="",
expected_output={},
)

assert str(exc_info.value) == "Failed to generate input: TimeoutError"
2 changes: 1 addition & 1 deletion packages/uipath/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading