Skip to content
Open
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 pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "uipath"
version = "2.6.30"
version = "2.7.0"
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
2 changes: 1 addition & 1 deletion src/uipath/_cli/cli_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ async def initialize() -> None:

if not entrypoints:
console.warning(
'No function entrypoints found. Add them to `uipath.json` under "functions": {"my_function": "src/main.py:main"}'
'No entrypoints found. Add them to `uipath.json` under "functions" or "agents": {"my_function": "src/main.py:main"}'
)

# Gather schemas from all discovered runtimes
Expand Down
7 changes: 7 additions & 0 deletions src/uipath/_cli/models/uipath_json_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ class UiPathJsonConfig(BaseModelWithDefaultConfig):
"Each key is an entrypoint name, and each value is a path in format 'file_path:function_name'",
)

agents: dict[str, str] = Field(
default_factory=dict,
description="Entrypoint definitions for agent scripts. "
"Each key is an entrypoint name, and each value is a path in format 'file_path:agent_name'",
)

def to_json_string(self, indent: int = 2) -> str:
"""Export to JSON string with proper formatting."""
return self.model_dump_json(
Expand All @@ -110,6 +116,7 @@ def create_default(cls) -> "UiPathJsonConfig":
include_uv_lock=True,
),
functions={},
agents={},
)

@classmethod
Expand Down
35 changes: 20 additions & 15 deletions src/uipath/functions/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,22 +44,18 @@ def _load_config(self) -> dict[str, Any]:
return self._config

def discover_entrypoints(self) -> list[str]:
"""Discover all function entrypoints from uipath.json."""
"""Discover all entrypoints (functions and agents) from uipath.json."""
config = self._load_config()
return list(config.get("functions", {}).keys())
functions = list(config.get("functions", {}).keys())
agents = list(config.get("agents", {}).keys())
return functions + agents

async def get_storage(self) -> UiPathRuntimeStorageProtocol | None:
"""Get storage protocol if any (placeholder for protocol compliance)."""
return None

async def get_settings(self) -> UiPathRuntimeFactorySettings | None:
"""Get factory settings for coded functions.

Coded functions don't need span filtering - all spans are relevant
since developers have full control over instrumentation.

Low-code agents (LangGraph) need filtering due to framework overhead.
"""
"""Get factory settings for coded functions."""
return None

async def new_runtime(
Expand All @@ -76,15 +72,22 @@ def _create_runtime(self, entrypoint: str) -> UiPathRuntimeProtocol:
"""Create runtime instance from entrypoint specification."""
config = self._load_config()
functions = config.get("functions", {})

if entrypoint not in functions:
agents = config.get("agents", {})

# Check both functions and agents
if entrypoint in functions:
func_spec = functions[entrypoint]
entrypoint_type = "function"
elif entrypoint in agents:
func_spec = agents[entrypoint]
entrypoint_type = "agent"
else:
available = list(functions.keys()) + list(agents.keys())
raise ValueError(
f"Entrypoint '{entrypoint}' not found in uipath.json. "
f"Available: {', '.join(functions.keys())}"
f"Available: {', '.join(available)}"
)

func_spec = functions[entrypoint]

if ":" not in func_spec:
raise ValueError(
f"Invalid function specification: '{func_spec}'. "
Expand All @@ -97,4 +100,6 @@ def _create_runtime(self, entrypoint: str) -> UiPathRuntimeProtocol:
if not full_path.exists():
raise ValueError(f"File not found: {full_path}")

return UiPathFunctionsRuntime(str(full_path), function_name, entrypoint)
return UiPathFunctionsRuntime(
str(full_path), function_name, entrypoint, entrypoint_type
)
21 changes: 18 additions & 3 deletions src/uipath/functions/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,25 @@
class UiPathFunctionsRuntime:
"""Runtime wrapper for a single Python function with full script executor compatibility."""

def __init__(self, file_path: str, function_name: str, entrypoint_name: str):
"""Initialize the function runtime."""
def __init__(
self,
file_path: str,
function_name: str,
entrypoint_name: str,
entrypoint_type: str = "function",
):
"""Initialize the function runtime.

Args:
file_path: Path to the Python file containing the function
function_name: Name of the function to execute
entrypoint_name: Name of the entrypoint
entrypoint_type: Type of entrypoint - 'function' or 'agent'
"""
self.file_path = Path(file_path)
self.function_name = function_name
self.entrypoint_name = entrypoint_name
self.entrypoint_type = entrypoint_type
self._function: Callable[..., Any] | None = None
self._module: ModuleType | None = None

Expand Down Expand Up @@ -180,10 +194,11 @@ async def get_schema(self) -> UiPathRuntimeSchema:
# Determine output schema
raw_output_schema = get_type_schema(hints.get("return"))
output_schema = transform_attachments(raw_output_schema)

return UiPathRuntimeSchema(
filePath=self.entrypoint_name,
uniqueId=str(uuid.uuid4()),
type="agent",
type=self.entrypoint_type,
input=input_schema,
output=output_schema,
)
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

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

Loading