Skip to content
Merged
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
60 changes: 60 additions & 0 deletions packages/uipath/docs/cli/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,66 @@ Treat `--client-secret` as a credential. In CI, prefer reading it from a secret

---

::: mkdocs-click
:module: uipath._cli
:command: new
:depth: 1
:style: table

Scaffolds a project in the current directory. `--type` selects what gets created:

- **`auto`** (default) — an installed agent framework integration (e.g. `uipath-langchain`) claims the scaffold and creates a coded agent project; with none installed, a coded function project is created.
- **`function`** — always creates a coded function project, regardless of installed integrations.
- **`agent`** — creates a coded agent project with the installed framework integration, and fails if none is installed rather than quietly creating a function project.

| Installed framework integrations | `uipath new x` (auto) | `uipath new x --type agent` |
|----------------------------------|------------------------|------------------------------|
| none | coded function project | error: install an agent framework |
| one | that framework's coded agent project | that framework's coded agent project |
| several | warns, scaffolds with the first discovered | warns, scaffolds with the first discovered |

With one framework installed there is nothing to choose, so `uipath new` uses it. With several, it warns and scaffolds with the first one discovered — pass `--agent-framework` to choose deliberately:

```shell
uipath new my-agent --type agent --agent-framework uipath-langchain
```

`--agent-framework` takes the integration package name, is only valid together with `--type agent`, and scaffolds with that framework alone. The names it accepts are the packages installed in your environment — the same ones the error above lists — so `uipath` keeps no list of its own.

Scaffold a coded function:

<!-- termynal -->

```shell
> uipath new my-function --type function
⠋ Creating new project my-function in current directory ...
✓ Created 'main.py' file.
✓ Created 'pyproject.toml' file.
✓ Created 'uipath.json' file.
💡 Initialize project: uipath init
💡 Run project: uipath run main '{"message": "Hello World!"}'
```

Scaffold a coded agent — requires the framework's integration package in the environment:

<!-- termynal -->

```shell
> uv add uipath-langchain
Resolved 42 packages in 1.2s
Installed 42 packages in 0.8s

> uipath new my-agent --type agent
⠋ Creating new agent my-agent in current directory ...
✓ Created 'main.py' file.
✓ Created 'langgraph.json' file.
✓ Created 'pyproject.toml' file.
💡 Initialize project: uipath init
💡 Run agent: uipath run agent '{"topic": "UiPath"}'
```

---

::: mkdocs-click
:module: uipath._cli
:command: init
Expand Down
17 changes: 17 additions & 0 deletions packages/uipath/docs/core/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ The `uipath` package is always required. Add one framework extension on top:
| PydanticAI | `uipath-pydantic-ai` | Type-safe agents with Pydantic models |
| Google ADK | `uipath-google-adk` | Gemini models, Google ecosystem |
| UiPath Agent Framework | `uipath-agent-framework` | UiPath-native agent primitives |
| Claude Agent SDK | `uipath-claude-sdk` | Claude models, Anthropic-native agent loop |

Installing one of these packages is what makes `uipath new` scaffold an agent. With more than one installed, name the one you want with `--agent-framework`, using the package exactly as it appears above.

---

Expand Down Expand Up @@ -85,6 +88,20 @@ The example below uses LangChain. Swap `uipath-langchain` for the framework of y

////

/// info | Guaranteeing an agent project
`uipath new` defaults to `--type auto`: the installed framework integration claims the scaffold, which is why the commands above create an agent project. Pass `--type agent` to make that a requirement — it fails with instructions instead of creating a function project when no framework is installed:

```shell
uipath new agent --type agent
```

With several framework integrations installed in the same environment, `uipath new` warns and uses the first one discovered; name the one you want instead with:

```shell
uipath new agent --type agent --agent-framework uipath-langchain
```
///

---

## Project Structure
Expand Down
8 changes: 8 additions & 0 deletions packages/uipath/docs/core/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@ Use coded functions for deterministic compute steps: document extraction, ERP wr

////

/// info | Guaranteeing a function project
`uipath new` defaults to `--type auto`: when an agent framework integration (e.g. `uipath-langchain`) is installed in the environment, it scaffolds a coded agent instead of a function. Pass `--type function` to always get a coded function project:

```shell
uipath new my-function --type function
```
///

---

## Project Structure
Expand Down
10 changes: 8 additions & 2 deletions packages/uipath/docs/core/studio_web.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ First, install the SDK package for the framework you want to use:
# uipath-pydantic-ai - PydanticAI
# uipath-google-adk - Google ADK
# uipath-agent-framework - UiPath Agent Framework
# uipath-claude-sdk - Claude Agent SDK
> uv add uipath-langchain
Resolved 42 packages in 1.2s
Installed 42 packages in 0.8s
Expand All @@ -190,6 +191,7 @@ Installed 42 packages in 0.8s
# uipath-pydantic-ai - PydanticAI
# uipath-google-adk - Google ADK
# uipath-agent-framework - UiPath Agent Framework
# uipath-claude-sdk - Claude Agent SDK
> pip install uipath-langchain
Successfully installed uipath-langchain
```
Expand Down Expand Up @@ -221,9 +223,13 @@ Selected tenant: Tenant1

That's it, your agent should now be visible in Studio Web.

/// info
`uipath new` defaults to `--type auto`, which lets the installed framework integration claim the scaffold. Pass `--type agent` to require an agent project — it fails rather than creating a function project when no framework is installed. With several integrations installed, `uipath new` warns and uses the first one discovered; pick explicitly with `uipath new agent --type agent --agent-framework uipath-langchain`.
///

#### Coded Function

A coded function doesn't require an additional framework package. Authenticate, scaffold the project, and initialize it:
A coded function doesn't require an additional framework package. Authenticate, scaffold the project, and initialize it (`--type function` guarantees a function project even when a framework integration is installed):

<!-- termynal -->

Expand All @@ -238,7 +244,7 @@ Select tenant number: 0
Selected tenant: Tenant1
✓ Authentication successful.

> uipath new my-function
> uipath new my-function --type function
✓ Created 'main.py' file.
✓ Created 'pyproject.toml' file.
✓ Created 'uipath.json' file.
Expand Down
2 changes: 1 addition & 1 deletion packages/uipath/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "uipath"
version = "2.14.22"
version = "2.14.23"
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
4 changes: 4 additions & 0 deletions packages/uipath/src/uipath/_cli/_utils/_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,7 @@
def is_binary_file(file_extension: str) -> bool:
"""Determine if a file should be treated as binary."""
return file_extension.lower() in BINARY_EXTENSIONS


# Supported agent frameworks and their packages, for `uipath new --type agent`
AGENT_FRAMEWORKS_DOCS_URL = "https://uipath.github.io/uipath-python/core/agents/"
126 changes: 116 additions & 10 deletions packages/uipath/src/uipath/_cli/cli_new.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import importlib.metadata
import json
import os
import shutil
Expand All @@ -9,8 +10,11 @@

from ._telemetry import track_command
from ._utils._console import ConsoleLogger
from ._utils._constants import AGENT_FRAMEWORKS_DOCS_URL
from ._utils._project_files import resolve_existing_project_id
from .middlewares import Middlewares
from .middlewares import MiddlewareResult, Middlewares
from .models.agent_frameworks import AgentFramework
from .models.project_types import ProjectType

console = ConsoleLogger()

Expand Down Expand Up @@ -57,10 +61,91 @@ def generate_uipath_json(target_directory):
json.dump(uipath_config, f, indent=2)


def installed_agent_frameworks() -> list[AgentFramework]:
"""Agent frameworks that can scaffold a project, in discovery order."""
packages: dict[str, str] = {}
for entry_point in importlib.metadata.entry_points(group="uipath.middlewares"):
if entry_point.dist is not None:
packages.setdefault(entry_point.module.split(".")[0], entry_point.dist.name)

frameworks = []
for middleware in Middlewares.get("new"):
package = packages.get(middleware.__module__.split(".")[0])
if package is not None:
frameworks.append(AgentFramework(package=package, scaffold=middleware))
return frameworks


def _select_agent_framework(
frameworks: list[AgentFramework], requested: str
) -> AgentFramework:
"""Resolve `--agent-framework` against what is installed."""
for framework in frameworks:
if requested == framework.package:
return framework

installed = (
"Installed: "
+ ", ".join(sorted(framework.package for framework in frameworks))
+ "."
if frameworks
else "No agent framework is installed."
)
console.error(
f"No installed agent framework matches '{requested}'.\n"
f"{installed}\n"
f"See {AGENT_FRAMEWORKS_DOCS_URL} for the supported frameworks and "
"their packages."
)


def _scaffold_agent(name: str, agent_framework: str | None) -> MiddlewareResult:
"""Offer the scaffold to the installed agent frameworks."""
Middlewares.load_plugins()
installed = installed_agent_frameworks()

if agent_framework:
# Dispatch to the chosen framework alone, so that another one cannot
# claim the scaffold ahead of it.
return _select_agent_framework(installed, agent_framework).scaffold(name)

if len(installed) > 1:
first = installed[0]
console.warning(
"Multiple agent frameworks are installed: "
+ ", ".join(framework.package for framework in installed)
+ f".\nScaffolding with the first one discovered: '{first.package}'. "
f"To pick a different one, run `uipath new {name} --type agent "
"--agent-framework <framework>`."
)
return first.scaffold(name)

return Middlewares.next("new", name)


@click.command()
@click.argument("name", type=str, default="")
@click.option(
"--type",
"project_type",
type=click.Choice([t.value for t in ProjectType]),
default=ProjectType.AUTO.value,
show_default=True,
help="Project type to scaffold. 'auto' scaffolds an agent when an agent "
"framework package (e.g. uipath-langchain) is installed and a function "
"otherwise; 'function' always scaffolds a function; 'agent' scaffolds an "
"agent and fails when no agent framework is installed.",
)
@click.option(
"--agent-framework",
"agent_framework",
default=None,
help="Agent framework to scaffold with, named by its package (e.g. "
"`uipath-langchain`). Only valid together with `--type agent`; picks "
"which framework scaffolds when several are installed.",
)
@track_command("new")
def new(name: str):
def new(name: str, project_type: str, agent_framework: str | None):
"""Generate a quick-start project."""
directory = os.getcwd()

Expand All @@ -69,18 +154,39 @@ def new(name: str):
"Please specify a name for your project:\n`uipath new hello-world`"
)

result = Middlewares.next("new", name)
scaffold_type = ProjectType(project_type)

if result.error_message:
if agent_framework and scaffold_type is not ProjectType.AGENT:
console.error(
result.error_message, include_traceback=result.should_include_stacktrace
"`--agent-framework` can only be used together with `--type agent`."
)

if result.info_message:
console.info(result.info_message)

if not result.should_continue:
return
# Agent frameworks scaffold through the `new` middleware chain. A function
# project never consults them.
if scaffold_type is not ProjectType.FUNCTION:
result = _scaffold_agent(name, agent_framework)

if result.error_message:
console.error(
result.error_message, include_traceback=result.should_include_stacktrace
)

if result.info_message:
console.info(result.info_message)

if not result.should_continue:
return # an agent framework scaffolded the project

if scaffold_type is ProjectType.AGENT:
console.error(
"No agent framework is installed, so there is nothing to "
"scaffold an agent with.\n"
"Install the framework you want to use and run this command "
f"again — see {AGENT_FRAMEWORKS_DOCS_URL} for the supported "
"frameworks and their packages.\n"
f"Or run `uipath new {name} --type function` to create a "
"function project."
)

with console.spinner(f"Creating new project {name} in current directory ..."):
generate_script(directory)
Expand Down
16 changes: 16 additions & 0 deletions packages/uipath/src/uipath/_cli/models/agent_frameworks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""Agent frameworks that scaffold projects for `uipath new`."""

from dataclasses import dataclass

from ..middlewares import MiddlewareFunc


@dataclass(frozen=True)
class AgentFramework:
"""An agent framework installed in this environment."""

package: str
"""Package that provides it, and the value `--agent-framework` takes."""

scaffold: MiddlewareFunc
"""Its `new` middleware."""
16 changes: 16 additions & 0 deletions packages/uipath/src/uipath/_cli/models/project_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""Project types scaffolded by `uipath new`."""

from enum import StrEnum


class ProjectType(StrEnum):
"""What `uipath new` scaffolds.

AUTO (the default) lets an installed agent framework claim the scaffold
and falls back to a function project; FUNCTION and AGENT request one
explicitly.
"""

AUTO = "auto"
FUNCTION = "function"
AGENT = "agent"
Loading
Loading