Skip to content

Commit 1c88aac

Browse files
committed
Merge branch 'main' into josh/cas-confirm-tool-call
Resolved conflicts in pyproject.toml and uv.lock — bumped version to 2.10.56.
2 parents 0422a8d + f82df05 commit 1c88aac

61 files changed

Lines changed: 5134 additions & 280 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/test-packages.yml

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,55 @@ jobs:
130130
working-directory: packages/uipath-platform
131131
run: uv run pytest
132132

133+
e2e-uipath-platform:
134+
name: E2E (uipath-platform, memory)
135+
needs: detect-changed-packages
136+
runs-on: ubuntu-latest
137+
steps:
138+
- name: Check if package changed
139+
id: check
140+
shell: bash
141+
run: |
142+
if echo '${{ needs.detect-changed-packages.outputs.packages }}' | jq -e 'index("uipath-platform")' > /dev/null; then
143+
echo "skip=false" >> $GITHUB_OUTPUT
144+
else
145+
echo "skip=true" >> $GITHUB_OUTPUT
146+
fi
147+
148+
- name: Skip
149+
if: steps.check.outputs.skip == 'true'
150+
shell: bash
151+
run: echo "Skipping - no changes to uipath-platform"
152+
153+
- name: Checkout
154+
if: steps.check.outputs.skip != 'true'
155+
uses: actions/checkout@v4
156+
157+
- name: Setup uv
158+
if: steps.check.outputs.skip != 'true'
159+
uses: astral-sh/setup-uv@v5
160+
161+
- name: Setup Python
162+
if: steps.check.outputs.skip != 'true'
163+
uses: actions/setup-python@v5
164+
with:
165+
python-version: "3.11"
166+
167+
- name: Install dependencies
168+
if: steps.check.outputs.skip != 'true'
169+
working-directory: packages/uipath-platform
170+
run: uv sync --all-extras --python 3.11
171+
172+
- name: Run E2E memory tests
173+
if: steps.check.outputs.skip != 'true'
174+
working-directory: packages/uipath-platform
175+
env:
176+
UIPATH_URL: ${{ secrets.ALPHA_BASE_URL }}
177+
UIPATH_CLIENT_ID: ${{ secrets.ALPHA_TEST_CLIENT_ID }}
178+
UIPATH_CLIENT_SECRET: ${{ secrets.ALPHA_TEST_CLIENT_SECRET }}
179+
UIPATH_FOLDER_KEY: ${{ secrets.UIPATH_MEMORY_FOLDER }}
180+
run: uv run pytest tests/services/test_memory_service_e2e.py -m e2e -v --no-cov
181+
133182
test-uipath:
134183
name: Test (uipath, ${{ matrix.python-version }}, ${{ matrix.os }})
135184
needs: detect-changed-packages
@@ -184,7 +233,7 @@ jobs:
184233

185234
test-gate:
186235
name: Test
187-
needs: [test-uipath-core, test-uipath-platform, test-uipath]
236+
needs: [test-uipath-core, test-uipath-platform, test-uipath, e2e-uipath-platform]
188237
runs-on: ubuntu-latest
189238
if: always()
190239
steps:
@@ -196,4 +245,8 @@ jobs:
196245
echo "Tests failed"
197246
exit 1
198247
fi
248+
# E2E tests are informational — log but don't block
249+
if [[ "${{ needs.e2e-uipath-platform.result }}" == "failure" ]]; then
250+
echo "⚠️ E2E memory tests failed (non-blocking)"
251+
fi
199252
echo "All tests passed"

packages/uipath-core/src/uipath/core/chat/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@
103103
UiPathConversationToolCallResult,
104104
UiPathConversationToolCallStartEvent,
105105
)
106+
from .voice import (
107+
UiPathVoiceToolCallMessage,
108+
UiPathVoiceToolCallRequest,
109+
UiPathVoiceToolCallResult,
110+
)
106111

107112
__all__ = [
108113
# Root
@@ -168,4 +173,8 @@
168173
"UiPathConversationAsyncInputStreamEvent",
169174
# Meta
170175
"UiPathConversationMetaEvent",
176+
# Voice
177+
"UiPathVoiceToolCallRequest",
178+
"UiPathVoiceToolCallMessage",
179+
"UiPathVoiceToolCallResult",
171180
]
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""Voice tool-call wire models (CAS socket.io)."""
2+
3+
from typing import Any
4+
5+
from pydantic import BaseModel, ConfigDict, Field
6+
7+
8+
class _VoiceWire(BaseModel):
9+
model_config = ConfigDict(validate_by_name=True, validate_by_alias=True)
10+
11+
12+
class UiPathVoiceToolCallRequest(_VoiceWire):
13+
"""Single tool call in a batch."""
14+
15+
call_id: str = Field(..., alias="callId")
16+
tool_name: str = Field(..., alias="toolName")
17+
args: dict[str, Any]
18+
19+
20+
class UiPathVoiceToolCallMessage(_VoiceWire):
21+
"""Batch of tool calls from CAS."""
22+
23+
calls: list[UiPathVoiceToolCallRequest] = Field(..., min_length=1)
24+
25+
26+
class UiPathVoiceToolCallResult(_VoiceWire):
27+
"""Result of a single tool call."""
28+
29+
result: str
30+
is_error: bool = Field(..., alias="isError")

packages/uipath-platform/pyproject.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "uipath-platform"
3-
version = "0.1.28"
3+
version = "0.1.37"
44
description = "HTTP client library for programmatic access to UiPath Platform"
55
readme = { file = "README.md", content-type = "text/markdown" }
66
requires-python = ">=3.11"
@@ -98,9 +98,12 @@ warn_required_dynamic_aliases = true
9898
[tool.pytest.ini_options]
9999
testpaths = ["tests"]
100100
python_files = "test_*.py"
101-
addopts = "-ra -q --cov=src/uipath --cov-report=term-missing"
101+
addopts = "-ra -q --cov=src/uipath --cov-report=term-missing -m 'not e2e'"
102102
asyncio_default_fixture_loop_scope = "function"
103103
asyncio_mode = "auto"
104+
markers = [
105+
"e2e: end-to-end tests against real ECS/LLMOps (requires UIPATH_URL, UIPATH_ACCESS_TOKEN, UIPATH_FOLDER_KEY)",
106+
]
104107

105108
[tool.coverage.report]
106109
show_missing = true

packages/uipath-platform/src/uipath/platform/_uipath.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from .action_center import TasksService
99
from .agenthub._agenthub_service import AgentHubService
1010
from .agenthub._remote_a2a_service import RemoteA2aService
11+
from .automation_ops import AutomationOpsService
1112
from .chat import ConversationsService, UiPathLlmChatService, UiPathOpenAIService
1213
from .common import (
1314
ApiClient,
@@ -22,6 +23,7 @@
2223
from .entities import EntitiesService
2324
from .errors import BaseUrlMissingError, SecretMissingError
2425
from .guardrails import GuardrailsService
26+
from .memory import MemoryService
2527
from .orchestrator import (
2628
AssetsService,
2729
AttachmentsService,
@@ -34,6 +36,7 @@
3436
QueuesService,
3537
)
3638
from .resource_catalog import ResourceCatalogService
39+
from .semantic_proxy import SemanticProxyService
3740

3841

3942
def _has_valid_client_credentials(
@@ -113,6 +116,10 @@ def context_grounding(self) -> ContextGroundingService:
113116
self.buckets,
114117
)
115118

119+
@property
120+
def memory(self) -> MemoryService:
121+
return MemoryService(self._config, self._execution_context, self.folders)
122+
116123
@property
117124
def documents(self) -> DocumentsService:
118125
return DocumentsService(self._config, self._execution_context)
@@ -173,6 +180,14 @@ def remote_a2a(self) -> RemoteA2aService:
173180
def orchestrator_setup(self) -> OrchestratorSetupService:
174181
return OrchestratorSetupService(self._config, self._execution_context)
175182

183+
@property
184+
def automation_ops(self) -> AutomationOpsService:
185+
return AutomationOpsService(self._config, self._execution_context)
186+
187+
@property
188+
def semantic_proxy(self) -> SemanticProxyService:
189+
return SemanticProxyService(self._config, self._execution_context)
190+
176191
@property
177192
def automation_tracker(self) -> AutomationTrackerService:
178193
return AutomationTrackerService(self._config, self._execution_context)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"""AutomationOps service package.
2+
3+
Provides the ``AutomationOpsService`` client for retrieving deployed AI Trust
4+
Layer policies from AgentHub.
5+
"""
6+
7+
from ._automation_ops_service import AutomationOpsService
8+
9+
__all__ = ["AutomationOpsService"]
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
"""AutomationOps service for UiPath Platform.
2+
3+
Provides methods for retrieving deployed policies from the AgentHub service.
4+
"""
5+
6+
from typing import Any
7+
8+
from uipath.core.tracing import traced
9+
10+
from ..common._base_service import BaseService
11+
from ..common._config import UiPathApiConfig
12+
from ..common._execution_context import UiPathExecutionContext
13+
from ..common._models import Endpoint, RequestSpec
14+
15+
_DEPLOYED_POLICY_ENDPOINT = Endpoint("agenthub_/api/policies/deployed-policy")
16+
17+
18+
class AutomationOpsService(BaseService):
19+
"""Service for interacting with UiPath AutomationOps policies via AgentHub."""
20+
21+
def __init__(
22+
self,
23+
config: UiPathApiConfig,
24+
execution_context: UiPathExecutionContext,
25+
) -> None:
26+
super().__init__(config=config, execution_context=execution_context)
27+
28+
@traced(name="automation_ops_get_deployed_policy", run_type="uipath")
29+
def get_deployed_policy(self) -> dict[str, Any]:
30+
"""Retrieve the deployed policy.
31+
32+
Returns:
33+
The deployed policy response as a dictionary.
34+
"""
35+
spec = self._deployed_policy_spec()
36+
response = self.request(
37+
spec.method,
38+
url=spec.endpoint,
39+
headers=spec.headers,
40+
scoped="tenant",
41+
)
42+
return response.json()
43+
44+
@traced(name="automation_ops_get_deployed_policy", run_type="uipath")
45+
async def get_deployed_policy_async(self) -> dict[str, Any]:
46+
"""Retrieve the deployed policy (async).
47+
48+
Returns:
49+
The deployed policy response as a dictionary.
50+
"""
51+
spec = self._deployed_policy_spec()
52+
response = await self.request_async(
53+
spec.method,
54+
url=spec.endpoint,
55+
headers=spec.headers,
56+
scoped="tenant",
57+
)
58+
return response.json()
59+
60+
def _deployed_policy_spec(self) -> RequestSpec:
61+
return RequestSpec(
62+
method="POST",
63+
endpoint=_DEPLOYED_POLICY_ENDPOINT,
64+
)

packages/uipath-platform/src/uipath/platform/common/_bindings.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,15 @@ def folder_identifier(self) -> str:
5252

5353
class GenericResourceOverwrite(ResourceOverwrite):
5454
resource_type: Literal[
55-
"process", "index", "app", "asset", "bucket", "mcpServer", "queue"
55+
"process",
56+
"index",
57+
"app",
58+
"asset",
59+
"bucket",
60+
"mcpServer",
61+
"queue",
62+
"remoteA2aAgent",
63+
"memorySpace",
5664
]
5765
name: str = Field(alias="name")
5866
folder_path: str = Field(alias="folderPath")

0 commit comments

Comments
 (0)