Skip to content

Commit b4cb2c8

Browse files
committed
feat(AgentDefinitions): adding utils to load usable agent definitions from cloud
Specifically, allow loading of low-code agents along with usable tool definitions.
1 parent 6edb2cb commit b4cb2c8

File tree

3 files changed

+796
-0
lines changed

3 files changed

+796
-0
lines changed

src/uipath/agent/_utils.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import logging
2+
from pathlib import PurePath
3+
4+
from httpx import Response
5+
from pydantic import TypeAdapter
6+
7+
from uipath._cli._utils._studio_project import (
8+
ProjectFile,
9+
ProjectFolder,
10+
StudioClient,
11+
resolve_path,
12+
)
13+
from uipath.agent.models.agent import AgentDefinition
14+
15+
logger = logging.getLogger(__name__)
16+
17+
18+
async def get_file(
19+
folder: ProjectFolder, path: PurePath, studio_client: StudioClient
20+
) -> Response:
21+
resolved = resolve_path(folder, path)
22+
assert isinstance(resolved, ProjectFile), "Path file not found."
23+
return await studio_client.download_file_async(resolved.id)
24+
25+
26+
async def load_agent_definition(project_id: str):
27+
studio_client = StudioClient(project_id=project_id)
28+
project_structure = await studio_client.get_project_structure_async()
29+
30+
agent = (
31+
await get_file(project_structure, PurePath("agent.json"), studio_client)
32+
).json()
33+
34+
resolved_path = resolve_path(project_structure, PurePath("resources"))
35+
if isinstance(resolved_path, ProjectFolder):
36+
resource_folders = resolved_path.folders
37+
else:
38+
logger.warning(
39+
"Unable to read resource information from project. Defaulting to empty resources."
40+
)
41+
resource_folders = []
42+
43+
resources = []
44+
for resource in resource_folders:
45+
resources.append(
46+
(await get_file(resource, PurePath("resource.json"), studio_client)).json()
47+
)
48+
49+
agent_definition = {
50+
"id": project_id,
51+
"name": project_structure.name,
52+
"resources": resources,
53+
**agent,
54+
}
55+
return TypeAdapter(AgentDefinition).validate_python(agent_definition)

src/uipath/agent/models/agent.py

Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
"""Agent Models."""
2+
3+
from enum import Enum
4+
from typing import Annotated, Any, Dict, List, Literal, Optional, Union
5+
6+
from pydantic import BaseModel, ConfigDict, Discriminator, Field, Tag
7+
8+
9+
class AgentResourceType(str, Enum):
10+
"""Enum for resource types."""
11+
12+
TOOL = "tool"
13+
CONTEXT = "context"
14+
ESCALATION = "escalation"
15+
16+
17+
class BaseAgentResourceConfig(BaseModel):
18+
"""Base resource model with common properties."""
19+
20+
name: str
21+
description: str
22+
23+
model_config = ConfigDict(
24+
validate_by_name=True, validate_by_alias=True, extra="allow"
25+
)
26+
27+
28+
class AgentUnknownResourceConfig(BaseAgentResourceConfig):
29+
"""Fallback for unknown or future resource types."""
30+
31+
resource_type: str = Field(alias="$resourceType")
32+
33+
model_config = ConfigDict(extra="allow")
34+
35+
36+
class BaseAgentToolResourceConfig(BaseAgentResourceConfig):
37+
"""Tool resource with tool-specific properties."""
38+
39+
resource_type: Literal[AgentResourceType.TOOL] = Field(alias="$resourceType")
40+
input_schema: Dict[str, Any] = Field(
41+
..., alias="inputSchema", description="Input schema for the tool"
42+
)
43+
44+
model_config = ConfigDict(
45+
validate_by_name=True, validate_by_alias=True, extra="allow"
46+
)
47+
48+
49+
class AgentToolType(str, Enum):
50+
"""Agent tool type."""
51+
52+
AGENT = "agent"
53+
INTEGRATION = "integration"
54+
55+
56+
class AgentToolSettings(BaseModel):
57+
"""Settings for tool configuration."""
58+
59+
max_attempts: Optional[int] = Field(None, alias="maxAttempts")
60+
retry_delay: Optional[int] = Field(None, alias="retryDelay")
61+
timeout: Optional[int] = Field(None)
62+
63+
model_config = ConfigDict(
64+
validate_by_name=True, validate_by_alias=True, extra="allow"
65+
)
66+
67+
68+
class AgentProcessToolProperties(BaseModel):
69+
"""Properties specific to tool configuration."""
70+
71+
folder_path: Optional[str] = Field(None, alias="folderPath")
72+
process_name: Optional[str] = Field(None, alias="processName")
73+
74+
model_config = ConfigDict(
75+
validate_by_name=True, validate_by_alias=True, extra="allow"
76+
)
77+
78+
79+
class AgentProcessToolResourceConfig(BaseAgentToolResourceConfig):
80+
"""Tool resource with tool-specific properties."""
81+
82+
type: Literal[AgentToolType.AGENT] = AgentToolType.AGENT
83+
output_schema: Dict[str, Any] = Field(
84+
..., alias="outputSchema", description="Output schema for the tool"
85+
)
86+
properties: AgentProcessToolProperties = Field(
87+
..., description="Tool-specific properties"
88+
)
89+
settings: AgentToolSettings = Field(
90+
default_factory=AgentToolSettings, description="Tool settings"
91+
)
92+
93+
model_config = ConfigDict(
94+
validate_by_name=True, validate_by_alias=True, extra="allow"
95+
)
96+
97+
98+
class AgentIntegrationToolResourceConfig(BaseAgentToolResourceConfig):
99+
"""Tool resource with tool-specific properties."""
100+
101+
type: Literal[AgentToolType.INTEGRATION] = AgentToolType.INTEGRATION
102+
103+
model_config = ConfigDict(
104+
validate_by_name=True, validate_by_alias=True, extra="allow"
105+
)
106+
107+
108+
class AgentUnknownToolResourceConfig(BaseAgentToolResourceConfig):
109+
"""Fallback for unknown or future tool types."""
110+
111+
resource_type: Literal[AgentResourceType.TOOL] = AgentResourceType.TOOL
112+
type: str = Field(alias="$resourceType")
113+
114+
model_config = ConfigDict(extra="allow")
115+
116+
117+
class AgentContextSettings(BaseModel):
118+
"""Settings for context configuration."""
119+
120+
result_count: int = Field(alias="resultCount")
121+
retrieval_mode: Literal["Semantic", "Structured"] = Field(alias="retrievalMode")
122+
threshold: float = Field(default=0)
123+
124+
model_config = ConfigDict(
125+
validate_by_name=True, validate_by_alias=True, extra="allow"
126+
)
127+
128+
129+
class AgentContextResourceConfig(BaseAgentResourceConfig):
130+
"""Context resource with context-specific properties."""
131+
132+
resource_type: Literal[AgentResourceType.CONTEXT] = Field(alias="$resourceType")
133+
folder_path: str = Field(alias="folderPath")
134+
index_name: str = Field(alias="indexName")
135+
settings: AgentContextSettings = Field(..., description="Context settings")
136+
137+
model_config = ConfigDict(
138+
validate_by_name=True, validate_by_alias=True, extra="allow"
139+
)
140+
141+
142+
class AgentEscalationChannelProperties(BaseModel):
143+
"""Agent escalation channel properties."""
144+
145+
app_name: str = Field(..., alias="appName")
146+
app_version: int = Field(..., alias="appVersion")
147+
folder_name: Optional[str] = Field(..., alias="folderName")
148+
resource_key: str = Field(..., alias="resourceKey")
149+
is_actionable_message_enabled: Optional[bool] = Field(
150+
None, alias="isActionableMessageEnabled"
151+
)
152+
actionable_message_meta_data: Optional[Any] = Field(
153+
None, alias="actionableMessageMetaData"
154+
)
155+
156+
model_config = ConfigDict(
157+
validate_by_name=True, validate_by_alias=True, extra="allow"
158+
)
159+
160+
161+
class AgentEscalationChannel(BaseModel):
162+
"""Agent escalation channel."""
163+
164+
id: str = Field(..., alias="id")
165+
name: str = Field(..., alias="name")
166+
type: str = Field(alias="type")
167+
description: str = Field(..., alias="description")
168+
input_schema: Dict[str, Any] = Field(
169+
..., alias="inputSchema", description="Input schema for the escalation channel"
170+
)
171+
output_schema: Dict[str, Any] = Field(
172+
...,
173+
alias="outputSchema",
174+
description="Output schema for the escalation channel",
175+
)
176+
properties: AgentEscalationChannelProperties = Field(..., alias="properties")
177+
178+
model_config = ConfigDict(
179+
validate_by_name=True, validate_by_alias=True, extra="allow"
180+
)
181+
182+
183+
class AgentEscalationResourceConfig(BaseAgentResourceConfig):
184+
"""Escalation resource with escalation-specific properties."""
185+
186+
resource_type: Literal[AgentResourceType.ESCALATION] = Field(alias="$resourceType")
187+
channels: List[AgentEscalationChannel] = Field(alias="channels")
188+
189+
# escalation_type: int = Field(..., alias="escalationType")
190+
is_agent_memory_enabled: bool = Field(alias="isAgentMemoryEnabled")
191+
192+
model_config = ConfigDict(
193+
validate_by_name=True, validate_by_alias=True, extra="allow"
194+
)
195+
196+
197+
def custom_discriminator(data: Any) -> str:
198+
"""Discriminator for resource types. This is required due to multi-key discrimination requirements for resources."""
199+
if isinstance(data, dict):
200+
resource_type = data.get("$resourceType")
201+
if resource_type == AgentResourceType.CONTEXT:
202+
return "AgentContextResourceConfig"
203+
elif resource_type == AgentResourceType.ESCALATION:
204+
return "AgentEscalationResourceConfig"
205+
elif resource_type == AgentResourceType.TOOL:
206+
tool_type = data.get("type")
207+
if tool_type == AgentToolType.AGENT:
208+
return "AgentProcessToolResourceConfig"
209+
elif tool_type == AgentToolType.INTEGRATION:
210+
return "AgentIntegrationToolResourceConfig"
211+
else:
212+
return "AgentUnknownToolResourceConfig"
213+
else:
214+
return "AgentUnknownResourceConfig"
215+
raise ValueError("Invalid discriminator values")
216+
217+
218+
AgentResourceConfig = Annotated[
219+
Union[
220+
Annotated[
221+
AgentProcessToolResourceConfig, Tag("AgentProcessToolResourceConfig")
222+
],
223+
Annotated[
224+
AgentIntegrationToolResourceConfig,
225+
Tag("AgentIntegrationToolResourceConfig"),
226+
],
227+
Annotated[
228+
AgentUnknownToolResourceConfig, Tag("AgentUnknownToolResourceConfig")
229+
],
230+
Annotated[AgentContextResourceConfig, Tag("AgentContextResourceConfig")],
231+
Annotated[AgentEscalationResourceConfig, Tag("AgentEscalationResourceConfig")],
232+
Annotated[AgentUnknownResourceConfig, Tag("AgentUnknownResourceConfig")],
233+
],
234+
Field(discriminator=Discriminator(custom_discriminator)),
235+
]
236+
237+
238+
class BaseAgentDefinition(BaseModel):
239+
"""Main agent model."""
240+
241+
id: str = Field(..., description="Agent id or project name")
242+
name: str = Field(..., description="Agent name or project name")
243+
input_schema: Dict[str, Any] = Field(
244+
..., alias="inputSchema", description="JSON schema for input arguments"
245+
)
246+
output_schema: Dict[str, Any] = Field(
247+
..., alias="outputSchema", description="JSON schema for output arguments"
248+
)
249+
version: str = Field("1.0.0", description="Agent version")
250+
resources: List[AgentResourceConfig] = Field(
251+
..., description="List of tools, context, and escalation resources"
252+
)
253+
254+
model_config = ConfigDict(
255+
validate_by_name=True, validate_by_alias=True, extra="allow"
256+
)
257+
258+
259+
class AgentType(str, Enum):
260+
"""Agent type."""
261+
262+
LOW_CODE = "lowCode"
263+
264+
265+
class AgentMessageRole(str, Enum):
266+
"""Enum for message roles."""
267+
268+
SYSTEM = "system"
269+
USER = "user"
270+
271+
272+
class AgentMessage(BaseModel):
273+
"""Message model for agent conversations."""
274+
275+
role: AgentMessageRole
276+
content: str
277+
278+
model_config = ConfigDict(
279+
validate_by_name=True, validate_by_alias=True, extra="allow"
280+
)
281+
282+
283+
class AgentSettings(BaseModel):
284+
"""Settings for agent configuration."""
285+
286+
engine: str = Field(..., description="Engine type, e.g., 'basic-v1'")
287+
model: str = Field(..., description="LLM model identifier")
288+
max_tokens: int = Field(
289+
..., alias="maxTokens", description="Maximum number of tokens"
290+
)
291+
temperature: float = Field(..., description="Temperature for response generation")
292+
293+
model_config = ConfigDict(
294+
validate_by_name=True, validate_by_alias=True, extra="allow"
295+
)
296+
297+
298+
class LowCodeAgentDefinition(BaseAgentDefinition):
299+
"""Low code agent definition."""
300+
301+
type: Literal[AgentType.LOW_CODE] = AgentType.LOW_CODE
302+
messages: List[AgentMessage] = Field(
303+
..., description="List of system and user messages"
304+
)
305+
features: List[Any] = Field(
306+
default_factory=list, description="Currently empty feature list"
307+
)
308+
settings: AgentSettings = Field(..., description="Agent settings configuration")
309+
310+
311+
KnownAgentDefinition = Annotated[
312+
Union[LowCodeAgentDefinition,],
313+
Field(discriminator="type"),
314+
]
315+
316+
317+
class UnknownAgentDefinition(BaseAgentDefinition):
318+
"""Fallback for unknown agent definitions."""
319+
320+
type: str
321+
322+
model_config = ConfigDict(extra="allow")
323+
324+
325+
AgentDefinition = Union[KnownAgentDefinition, UnknownAgentDefinition]

0 commit comments

Comments
 (0)