diff --git a/packages/uipath-platform/pyproject.toml b/packages/uipath-platform/pyproject.toml index f9e9d14a0..99c6d16ab 100644 --- a/packages/uipath-platform/pyproject.toml +++ b/packages/uipath-platform/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath-platform" -version = "0.1.45" +version = "0.1.46" description = "HTTP client library for programmatic access to UiPath Platform" readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.11" diff --git a/packages/uipath-platform/src/uipath/platform/connections/_connections_service.py b/packages/uipath-platform/src/uipath/platform/connections/_connections_service.py index af3bb4f78..8ff9a82e7 100644 --- a/packages/uipath-platform/src/uipath/platform/connections/_connections_service.py +++ b/packages/uipath-platform/src/uipath/platform/connections/_connections_service.py @@ -8,7 +8,7 @@ from ..common._base_service import BaseService from ..common._bindings import resource_override -from ..common._config import UiPathApiConfig +from ..common._config import UiPathApiConfig, UiPathConfig from ..common._execution_context import UiPathExecutionContext from ..common._folder_context import header_folder from ..common._models import Endpoint, RequestSpec @@ -24,6 +24,13 @@ logger: logging.Logger = logging.getLogger("uipath") +HEADER_ORIGINATOR = "x-uipath-originator" +HEADER_SOURCE = "x-uipath-source" +# Sent on outbound Integration Service activity invocations so GenAI activities +# can be stitched back to the parent job for licensing attribution. +HEADER_ACTIVITY_JOB_ID = "x-uipath-job-id" +_ORIGINATOR_VALUE = "uipath-python" + class ConnectionsService(BaseService): """Service for managing UiPath external service connections. @@ -768,11 +775,13 @@ def _build_activity_request_spec( # header parameter handling headers = { - "x-uipath-originator": "uipath-python", - "x-uipath-source": "uipath-python", + HEADER_ORIGINATOR: _ORIGINATOR_VALUE, + HEADER_SOURCE: _ORIGINATOR_VALUE, **header_folder(folder_key, None), **header_params, } + if job_key := UiPathConfig.job_key: + headers[HEADER_ACTIVITY_JOB_ID] = job_key # body and files handling json_data: Dict[str, Any] | None = None diff --git a/packages/uipath-platform/tests/services/test_connections_service.py b/packages/uipath-platform/tests/services/test_connections_service.py index 75c89b517..d037c9372 100644 --- a/packages/uipath-platform/tests/services/test_connections_service.py +++ b/packages/uipath-platform/tests/services/test_connections_service.py @@ -17,7 +17,10 @@ ConnectionToken, EventArguments, ) -from uipath.platform.connections._connections_service import ConnectionsService +from uipath.platform.connections._connections_service import ( + HEADER_ACTIVITY_JOB_ID, + ConnectionsService, +) from uipath.platform.orchestrator._folder_service import FolderService @@ -1421,6 +1424,60 @@ def test_invoke_activity_sets_standard_headers( assert sent_request.headers["x-uipath-originator"] == "uipath-python" assert sent_request.headers["x-uipath-source"] == "uipath-python" + def test_invoke_activity_propagates_job_id_header( + self, + httpx_mock: HTTPXMock, + service: ConnectionsService, + simple_activity_metadata: ActivityMetadata, + monkeypatch: pytest.MonkeyPatch, + ) -> None: + """Activity invocations carry x-uipath-job-id so GenAI calls can be stitched for licensing.""" + monkeypatch.setenv("UIPATH_JOB_KEY", "job-key-abc") + connection_id = "test-connection-123" + + httpx_mock.add_response( + method="GET", + status_code=200, + json={"id": connection_id, "name": "Test", "elementInstanceId": 1}, + ) + httpx_mock.add_response(method="POST", status_code=200, json={}) + + service.invoke_activity( + activity_metadata=simple_activity_metadata, + connection_id=connection_id, + activity_input={"body_field1": "x"}, + ) + + sent_request = httpx_mock.get_requests()[1] + assert sent_request.headers[HEADER_ACTIVITY_JOB_ID] == "job-key-abc" + + def test_invoke_activity_omits_job_id_header_when_unset( + self, + httpx_mock: HTTPXMock, + service: ConnectionsService, + simple_activity_metadata: ActivityMetadata, + monkeypatch: pytest.MonkeyPatch, + ) -> None: + """No job-id header is sent when UIPATH_JOB_KEY is not set.""" + monkeypatch.delenv("UIPATH_JOB_KEY", raising=False) + connection_id = "test-connection-123" + + httpx_mock.add_response( + method="GET", + status_code=200, + json={"id": connection_id, "name": "Test", "elementInstanceId": 1}, + ) + httpx_mock.add_response(method="POST", status_code=200, json={}) + + service.invoke_activity( + activity_metadata=simple_activity_metadata, + connection_id=connection_id, + activity_input={"body_field1": "x"}, + ) + + sent_request = httpx_mock.get_requests()[1] + assert HEADER_ACTIVITY_JOB_ID not in sent_request.headers + def test_invoke_activity_with_body_fields( self, httpx_mock: HTTPXMock, diff --git a/packages/uipath-platform/uv.lock b/packages/uipath-platform/uv.lock index ae35f75a9..dbea2b79a 100644 --- a/packages/uipath-platform/uv.lock +++ b/packages/uipath-platform/uv.lock @@ -1088,7 +1088,7 @@ dev = [ [[package]] name = "uipath-platform" -version = "0.1.45" +version = "0.1.46" source = { editable = "." } dependencies = [ { name = "httpx" }, diff --git a/packages/uipath/uv.lock b/packages/uipath/uv.lock index 6cbe534e9..4339a51d2 100644 --- a/packages/uipath/uv.lock +++ b/packages/uipath/uv.lock @@ -2682,7 +2682,7 @@ dev = [ [[package]] name = "uipath-platform" -version = "0.1.45" +version = "0.1.46" source = { editable = "../uipath-platform" } dependencies = [ { name = "httpx" },