diff --git a/packages/uipath-platform/pyproject.toml b/packages/uipath-platform/pyproject.toml index 1dfa88405..5a226c03b 100644 --- a/packages/uipath-platform/pyproject.toml +++ b/packages/uipath-platform/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath-platform" -version = "0.0.25" +version = "0.0.26" 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/_uipath.py b/packages/uipath-platform/src/uipath/platform/_uipath.py index 6cb2b118e..87c3a17f0 100644 --- a/packages/uipath-platform/src/uipath/platform/_uipath.py +++ b/packages/uipath-platform/src/uipath/platform/_uipath.py @@ -7,6 +7,7 @@ from .action_center import TasksService from .agenthub._agenthub_service import AgentHubService +from .agenthub._remote_a2a_service import RemoteA2aService from .chat import ConversationsService, UiPathLlmChatService, UiPathOpenAIService from .common import ( ApiClient, @@ -162,6 +163,10 @@ def guardrails(self) -> GuardrailsService: def agenthub(self) -> AgentHubService: return AgentHubService(self._config, self._execution_context, self.folders) + @property + def remote_a2a(self) -> RemoteA2aService: + return RemoteA2aService(self._config, self._execution_context, self.folders) + @property def orchestrator_setup(self) -> OrchestratorSetupService: return OrchestratorSetupService(self._config, self._execution_context) diff --git a/packages/uipath-platform/src/uipath/platform/agenthub/__init__.py b/packages/uipath-platform/src/uipath/platform/agenthub/__init__.py index 2012f3953..426bec295 100644 --- a/packages/uipath-platform/src/uipath/platform/agenthub/__init__.py +++ b/packages/uipath-platform/src/uipath/platform/agenthub/__init__.py @@ -1,8 +1,10 @@ -"""UiPath AgentHub Models. +"""UiPath AgentHub Models and Services. -This module contains models related to UiPath AgentHub service. +This module contains models and services related to UiPath AgentHub. """ +from uipath.platform.agenthub._remote_a2a_service import RemoteA2aService from uipath.platform.agenthub.agenthub import LlmModel +from uipath.platform.agenthub.remote_a2a import RemoteA2aAgent, RemoteA2aAgentFolder -__all__ = ["LlmModel"] +__all__ = ["LlmModel", "RemoteA2aAgent", "RemoteA2aAgentFolder", "RemoteA2aService"] diff --git a/packages/uipath-platform/src/uipath/platform/agenthub/_remote_a2a_service.py b/packages/uipath-platform/src/uipath/platform/agenthub/_remote_a2a_service.py new file mode 100644 index 000000000..c9475976c --- /dev/null +++ b/packages/uipath-platform/src/uipath/platform/agenthub/_remote_a2a_service.py @@ -0,0 +1,283 @@ +"""Service for managing Remote A2A agents in UiPath AgentHub. + +.. warning:: + This module is experimental and subject to change. + The Remote A2A feature is in preview and its API may change in future releases. +""" + +import warnings +from typing import Any, List + +from ..common._base_service import BaseService +from ..common._config import UiPathApiConfig +from ..common._execution_context import UiPathExecutionContext +from ..common._folder_context import FolderContext, header_folder +from ..common._models import Endpoint, RequestSpec +from ..orchestrator import FolderService +from .remote_a2a import RemoteA2aAgent + + +class RemoteA2aService(FolderContext, BaseService): + """Service for managing Remote A2A agents in UiPath AgentHub. + + .. warning:: + This service is experimental and subject to change. + """ + + def __init__( + self, + config: UiPathApiConfig, + execution_context: UiPathExecutionContext, + folders_service: FolderService, + ) -> None: + super().__init__(config=config, execution_context=execution_context) + self._folders_service = folders_service + + def list( + self, + *, + top: int | None = None, + skip: int | None = None, + search: str | None = None, + folder_path: str | None = None, + ) -> List[RemoteA2aAgent]: + """List Remote A2A agents. + + .. warning:: + This method is experimental and subject to change. + + When called without folder_path, returns all agents across + the tenant that the user has access to. When called with a folder, + returns only agents in that folder. + + Args: + top: Maximum number of agents to return. + skip: Number of agents to skip (for pagination). + search: Filter agents by name or slug. + folder_path: Optional folder path to scope the query. + + Returns: + List[RemoteA2aAgent]: A list of Remote A2A agents. + + Examples: + ```python + from uipath import UiPath + + client = UiPath() + + # List all agents across tenant + agents = client.remote_a2a.list() + for agent in agents: + print(f"{agent.name} - {agent.slug}") + + # List with folder scope + agents = client.remote_a2a.list(folder_path="MyFolder") + ``` + """ + warnings.warn( + "remote_a2a.list is experimental and subject to change.", + stacklevel=2, + ) + spec = self._list_spec( + top=top, + skip=skip, + search=search, + folder_path=folder_path, + ) + response = self.request( + spec.method, + url=spec.endpoint, + params=spec.params, + headers=spec.headers, + ) + data = response.json() + return [RemoteA2aAgent.model_validate(agent) for agent in data.get("value", [])] + + async def list_async( + self, + *, + top: int | None = None, + skip: int | None = None, + search: str | None = None, + folder_path: str | None = None, + ) -> List[RemoteA2aAgent]: + """Asynchronously list Remote A2A agents. + + .. warning:: + This method is experimental and subject to change. + + Args: + top: Maximum number of agents to return. + skip: Number of agents to skip (for pagination). + search: Filter agents by name or slug. + folder_path: Optional folder path to scope the query. + + Returns: + List[RemoteA2aAgent]: A list of Remote A2A agents. + + Examples: + ```python + import asyncio + from uipath import UiPath + + sdk = UiPath() + + async def main(): + agents = await sdk.remote_a2a.list_async() + for agent in agents: + print(f"{agent.name} - {agent.slug}") + + asyncio.run(main()) + ``` + """ + warnings.warn( + "remote_a2a.list_async is experimental and subject to change.", + stacklevel=2, + ) + spec = self._list_spec( + top=top, + skip=skip, + search=search, + folder_path=folder_path, + ) + response = await self.request_async( + spec.method, + url=spec.endpoint, + params=spec.params, + headers=spec.headers, + ) + data = response.json() + return [RemoteA2aAgent.model_validate(agent) for agent in data.get("value", [])] + + def retrieve( + self, + slug: str, + *, + folder_path: str | None = None, + ) -> RemoteA2aAgent: + """Retrieve a specific Remote A2A agent by slug. + + .. warning:: + This method is experimental and subject to change. + + Args: + slug: The unique slug identifier for the agent. + folder_path: The folder path where the agent is located. + + Returns: + RemoteA2aAgent: The Remote A2A agent. + + Examples: + ```python + from uipath import UiPath + + client = UiPath() + + agent = client.remote_a2a.retrieve("weather", folder_path="MyFolder") + print(f"Agent: {agent.name}, URL: {agent.a2a_url}") + ``` + """ + warnings.warn( + "remote_a2a.retrieve is experimental and subject to change.", + stacklevel=2, + ) + spec = self._retrieve_spec(slug=slug, folder_path=folder_path) + response = self.request( + spec.method, + url=spec.endpoint, + params=spec.params, + headers=spec.headers, + ) + return RemoteA2aAgent.model_validate(response.json()) + + async def retrieve_async( + self, + slug: str, + *, + folder_path: str | None = None, + ) -> RemoteA2aAgent: + """Asynchronously retrieve a specific Remote A2A agent by slug. + + .. warning:: + This method is experimental and subject to change. + + Args: + slug: The unique slug identifier for the agent. + folder_path: The folder path where the agent is located. + + Returns: + RemoteA2aAgent: The Remote A2A agent. + + Examples: + ```python + import asyncio + from uipath import UiPath + + sdk = UiPath() + + async def main(): + agent = await sdk.remote_a2a.retrieve_async("weather", folder_path="MyFolder") + print(f"Agent: {agent.name}, URL: {agent.a2a_url}") + + asyncio.run(main()) + ``` + """ + warnings.warn( + "remote_a2a.retrieve_async is experimental and subject to change.", + stacklevel=2, + ) + spec = self._retrieve_spec(slug=slug, folder_path=folder_path) + response = await self.request_async( + spec.method, + url=spec.endpoint, + params=spec.params, + headers=spec.headers, + ) + return RemoteA2aAgent.model_validate(response.json()) + + @property + def custom_headers(self) -> dict[str, str]: + return self.folder_headers + + def _list_spec( + self, + *, + top: int | None, + skip: int | None, + search: str | None, + folder_path: str | None, + ) -> RequestSpec: + headers = {} + if folder_path is not None: + folder_key = self._folders_service.retrieve_folder_key(folder_path) + headers = header_folder(folder_key, None) + + params: dict[str, Any] = {} + if top is not None: + params["top"] = top + if skip is not None: + params["skip"] = skip + if search is not None: + params["search"] = search + + return RequestSpec( + method="GET", + endpoint=Endpoint("/agenthub_/api/remote-a2a-agents"), + params=params, + headers=headers, + ) + + def _retrieve_spec( + self, + slug: str, + *, + folder_path: str | None, + ) -> RequestSpec: + folder_key = self._folders_service.retrieve_folder_key(folder_path) + return RequestSpec( + method="GET", + endpoint=Endpoint(f"/agenthub_/api/remote-a2a-agents/{slug}"), + headers={ + **header_folder(folder_key, None), + }, + ) diff --git a/packages/uipath-platform/src/uipath/platform/agenthub/remote_a2a.py b/packages/uipath-platform/src/uipath/platform/agenthub/remote_a2a.py new file mode 100644 index 000000000..c81a7f4b2 --- /dev/null +++ b/packages/uipath-platform/src/uipath/platform/agenthub/remote_a2a.py @@ -0,0 +1,59 @@ +"""Models for Remote A2A Agents in UiPath AgentHub. + +.. warning:: + This module is experimental and subject to change. + The Remote A2A feature is in preview and its API may change in future releases. +""" + +from datetime import datetime +from typing import Any, Optional + +from pydantic import BaseModel, ConfigDict, Field +from pydantic.alias_generators import to_camel + + +class RemoteA2aAgentFolder(BaseModel): + """Folder information for a Remote A2A agent.""" + + model_config = ConfigDict( + alias_generator=to_camel, + populate_by_name=True, + use_enum_values=True, + arbitrary_types_allowed=True, + extra="allow", + ) + + key: Optional[str] = None + display_name: Optional[str] = None + fully_qualified_name: Optional[str] = None + + +class RemoteA2aAgent(BaseModel): + """Model representing a Remote A2A agent in UiPath AgentHub. + + .. warning:: + This model is experimental and subject to change. + """ + + model_config = ConfigDict( + alias_generator=to_camel, + populate_by_name=True, + use_enum_values=True, + arbitrary_types_allowed=True, + extra="allow", + ) + + id: Optional[str] = None + name: Optional[str] = None + slug: Optional[str] = None + description: Optional[str] = None + agent_card_url: Optional[str] = None + a2a_url: Optional[str] = Field(None, alias="a2aUrl") + folder: Optional[RemoteA2aAgentFolder] = None + headers: Optional[str] = None + is_active: Optional[bool] = None + cached_agent_card: Optional[Any] = None + created_at: Optional[datetime] = None + created_by: Optional[str] = None + updated_at: Optional[datetime] = None + updated_by: Optional[str] = None diff --git a/packages/uipath-platform/uv.lock b/packages/uipath-platform/uv.lock index 62fa875fd..60ec092d4 100644 --- a/packages/uipath-platform/uv.lock +++ b/packages/uipath-platform/uv.lock @@ -1088,7 +1088,7 @@ dev = [ [[package]] name = "uipath-platform" -version = "0.0.25" +version = "0.0.26" source = { editable = "." } dependencies = [ { name = "httpx" }, diff --git a/packages/uipath/pyproject.toml b/packages/uipath/pyproject.toml index d141a81de..662d00e5a 100644 --- a/packages/uipath/pyproject.toml +++ b/packages/uipath/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath" -version = "2.10.16" +version = "2.10.17" 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" diff --git a/packages/uipath/uv.lock b/packages/uipath/uv.lock index 30b9106ab..fcbb5d75d 100644 --- a/packages/uipath/uv.lock +++ b/packages/uipath/uv.lock @@ -2540,7 +2540,7 @@ wheels = [ [[package]] name = "uipath" -version = "2.10.16" +version = "2.10.17" source = { editable = "." } dependencies = [ { name = "applicationinsights" }, @@ -2679,7 +2679,7 @@ dev = [ [[package]] name = "uipath-platform" -version = "0.0.25" +version = "0.0.26" source = { editable = "../uipath-platform" } dependencies = [ { name = "httpx" },