Skip to content
Closed
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 packages/uipath-platform/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "uipath-platform"
version = "0.1.2"
version = "0.1.3"
description = "HTTP client library for programmatic access to UiPath Platform"
readme = { file = "README.md", content-type = "text/markdown" }
requires-python = ">=3.11"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from ..errors import EnrichedException

RETRYABLE_STATUS_CODES: frozenset[int] = frozenset({408, 429, 502, 503, 504})
RETRYABLE_STATUS_CODES: frozenset[int] = frozenset({408, 429, 502, 503, 504, 524})
NON_RETRYABLE_STATUS_CODES: frozenset[int] = frozenset({400, 401, 403, 404, 413, 422})


Expand Down
41 changes: 20 additions & 21 deletions packages/uipath-platform/tests/services/test_retry.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from uipath.platform.common.retry import (
_MAX_BACKOFF_DELAY,
_MAX_RETRY_AFTER_DELAY,
RETRYABLE_STATUS_CODES,
exponential_backoff_with_jitter,
extract_retry_after_from_chain,
is_retryable_platform_exception,
Expand All @@ -15,26 +14,6 @@
from uipath.platform.errors import EnrichedException


class TestRetryableStatusCodes:
def test_contains_expected_codes(self):
assert 408 in RETRYABLE_STATUS_CODES
assert 429 in RETRYABLE_STATUS_CODES
assert 502 in RETRYABLE_STATUS_CODES
assert 503 in RETRYABLE_STATUS_CODES
assert 504 in RETRYABLE_STATUS_CODES

def test_does_not_contain_non_retryable(self):
assert 400 not in RETRYABLE_STATUS_CODES
assert 401 not in RETRYABLE_STATUS_CODES
assert 403 not in RETRYABLE_STATUS_CODES
assert 404 not in RETRYABLE_STATUS_CODES
assert 500 not in RETRYABLE_STATUS_CODES
assert 501 not in RETRYABLE_STATUS_CODES

def test_is_frozenset(self):
assert isinstance(RETRYABLE_STATUS_CODES, frozenset)


class TestParseRetryAfter:
def test_valid_integer(self):
assert parse_retry_after("120") == 120.0
Expand Down Expand Up @@ -170,16 +149,36 @@ def test_timeout_exception(self):
err = httpx.TimeoutException("timed out")
assert is_retryable_platform_exception(err) is True

def test_enriched_408(self):
http_err = _make_http_status_error(408)
err = EnrichedException(http_err)
assert is_retryable_platform_exception(err) is True

def test_enriched_429(self):
http_err = _make_http_status_error(429)
err = EnrichedException(http_err)
assert is_retryable_platform_exception(err) is True

def test_enriched_502(self):
http_err = _make_http_status_error(502)
err = EnrichedException(http_err)
assert is_retryable_platform_exception(err) is True

def test_enriched_503(self):
http_err = _make_http_status_error(503)
err = EnrichedException(http_err)
assert is_retryable_platform_exception(err) is True

def test_enriched_504(self):
http_err = _make_http_status_error(504)
err = EnrichedException(http_err)
assert is_retryable_platform_exception(err) is True

def test_enriched_524_cloudflare_timeout(self):
http_err = _make_http_status_error(524)
err = EnrichedException(http_err)
assert is_retryable_platform_exception(err) is True

def test_enriched_400_not_retryable(self):
http_err = _make_http_status_error(400)
err = EnrichedException(http_err)
Expand Down
2 changes: 1 addition & 1 deletion packages/uipath-platform/uv.lock

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

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
244 changes: 244 additions & 0 deletions packages/uipath/docs/core/studio_web.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
# Studio Web Integration

[Studio Web](https://docs.uipath.com/studio-web/automation-cloud/latest/user-guide/overview) is a cloud IDE for building projects such as RPAs, low code agents, and API workflows. It also supports importing coded agents built locally. Bringing your coded agent into Studio Web gives you:

- Cloud debugging with dynamic breakpoints
- Running and defining evaluations directly in the cloud
- A unified build experience alongside multiple project types
- Self contained solution deployment units

<picture data-light="../assets/studio_web_diagram_light.png" data-dark="../assets/studio_web_diagram_dark.png">
<source
media="(prefers-color-scheme: dark)"
srcset="
../assets/studio_web_diagram_dark.png
"
/>
<img
src="../assets/studio_web_diagram_light.png"
alt="Coded agent in Studio Web"
/>
</picture>

There are two ways to connect a coded agent to Studio Web: using a [Cloud Workspace](#cloud-workspace) or a [Local Workspace](#local-workspace).

---

## Cloud Workspace

In a Cloud Workspace, your project lives in Studio Web and you sync code between your local IDE and the cloud.

### Importing a Coded Agent

1. Open your solution in Studio Web
2. Create a new Agent and select **Coded**

<picture data-light="../assets/studio_web_select_coded_light.png" data-dark="../assets/studio_web_select_coded_dark.png">
<source
media="(prefers-color-scheme: dark)"
srcset="
../assets/studio_web_select_coded_dark.png
"
/>
<img
src="../assets/studio_web_select_coded_light.png"
/>
</picture>

3. Choose a sample project to start from, or push an existing local agent

### Pushing an Existing Agent

If you already have a coded agent locally, you can sync it to Studio Web:

1. Copy the `UIPATH_PROJECT_ID` from Studio Web into your `.env` file

<picture data-light="../assets/studio_web_sync_from_ide_light.png" data-dark="../assets/studio_web_sync_from_ide_dark.png">
<source
media="(prefers-color-scheme: dark)"
srcset="
../assets/studio_web_sync_from_ide_dark.png
"
/>
<img
src="../assets/studio_web_sync_from_ide_light.png"
/>
</picture>

2. Push your project:

<!-- termynal -->
```shell
> uipath push
Pushing UiPath project to Studio Web...
Uploading 'main.py'
Uploading 'uipath.json'
Updating 'pyproject.toml'
Uploading '.uipath/studio_metadata.json'
Importing referenced resources to Studio Web project...
🔵 Resource import summary: 3 total resources - 1 created, 1 updated, 1 unchanged, 0 not found
```

Notice the **Resource import summary** at the end. The push command also imports resources defined in `bindings.json` into the Studio Web solution, just like importing resources for a low code agent. This ensures that all required resources are packaged with the solution, so the coded agent works anywhere the solution is deployed.

See [`uipath push`](../cli/index.md) in the CLI Reference.

### Pulling Changes

To pull the latest version from Studio Web to your local environment:

<!-- termynal -->
```shell
> uipath pull
Pulling UiPath project from Studio Web...
Processing: main.py
File 'main.py' is up to date
Processing: uipath.json
File 'uipath.json' is up to date
Processing: bindings.json
File 'bindings.json' is up to date
Processing: evaluations\eval-sets\evaluation-set-default.json
Downloaded 'evaluations\eval-sets\evaluation-set-default.json'
Processing: evaluations\evaluators\evaluator-default.json
Downloaded 'evaluations\evaluators\evaluator-default.json'
✓ Project pulled successfully
```

See [`uipath pull`](../cli/index.md) in the CLI Reference.

---

## Local Workspace

!!! warning "Preview Feature"
The local workspace integration is currently experimental.
Behavior is subject to change in future versions.

In a Local Workspace, your project lives on your machine and is linked to a Studio Web solution. See the [Local Workspace documentation](https://docs.uipath.com/studio-web/automation-cloud/latest/user-guide/solutions-in-the-local-workspace) for setup details.

You can either start from a predefined template in Studio Web or set up a new agent from scratch.

### Starting from a Template

When creating a new Coded agent in Studio Web with a Local Workspace, you can pick one of the predefined templates. This creates the project files directly on your machine. Templates come with sample code and predefined evaluations you can run immediately.

### Setting Up a New Agent

You can also create a coded agent from scratch in your local IDE and have it appear in Studio Web.

First, install the SDK package for the framework you want to use:

//// tab | uv

<!-- termynal -->
```shell
# Pick the package that matches your framework:
# uipath-langchain - LangChain / LangGraph
# uipath-openai-agents - OpenAI Agents SDK
# uipath-llamaindex - LlamaIndex
# uipath-pydantic-ai - PydanticAI
# uipath-google-adk - Google ADK
# uipath-agent-framework - UiPath Agent Framework
> uv add uipath-langchain
Resolved 42 packages in 1.2s
Installed 42 packages in 0.8s
```

////

//// tab | pip

<!-- termynal -->
```shell
# Pick the package that matches your framework:
# uipath-langchain - LangChain / LangGraph
# uipath-openai-agents - OpenAI Agents SDK
# uipath-llamaindex - LlamaIndex
# uipath-pydantic-ai - PydanticAI
# uipath-google-adk - Google ADK
# uipath-agent-framework - UiPath Agent Framework
> pip install uipath-langchain
Successfully installed uipath-langchain
```

////

Then authenticate, scaffold the agent, and initialize the project:

<!-- termynal -->
```shell
> uipath auth
⠋ Authenticating with UiPath ...
🔗 If a browser window did not open, please open the following URL in your browser: [LINK]
👇 Select tenant:
0: Tenant1
1: Tenant2
Select tenant number: 0
Selected tenant: Tenant1
✓ Authentication successful.

> uipath new agent
✓ Created new agent project.

> uipath init
⠋ Initializing UiPath project ...
✓ Created 'entry-points.json' file.
```

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

---

## Publishing

Once your coded agent is in Studio Web, publishing works the same as any other project. Click **Publish** in Studio Web and it will be packaged and deployed through the standard workflow.

---

## Running and Debugging

Your agent can be run both in the cloud (via Studio Web) and locally using the CLI.

### Running Locally

<!-- termynal -->
```shell
> uipath run agent '{"message": "hello"}'
```

See [`uipath run`](../cli/index.md) in the CLI Reference.

### Debugging Locally

Use `uipath debug` for an enhanced local debugging experience. Unlike `uipath run`, the debug command:

- Auto polls for trigger responses when the agent suspends (e.g., LangGraph interrupts)
- Fetches binding overwrites from Studio Web (configurable in **Debug > Debug Configuration > Solution resources**)

<!-- termynal -->
```shell
> uipath debug agent '{"message": "hello"}'
```

See [`uipath debug`](../cli/index.md) in the CLI Reference.

### Evaluating Locally

Run evaluations against your agent using the CLI:

<!-- termynal -->
```shell
> uipath eval agent .\evaluations\eval-sets\faithfulness-multi-model.json
```

See [`uipath eval`](../cli/index.md) in the CLI Reference and the [Evaluations documentation](../eval/index.md).

---

## Syncing Evaluations

Evaluations can be defined either in Studio Web or locally. They sync automatically when you use `uipath pull` and `uipath push`.

/// note
Custom evaluators must be created locally. See [Custom Evaluators](../eval/custom_evaluators.md) for details.
///
1 change: 1 addition & 0 deletions packages/uipath/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ nav:
- Environment Variables: core/environment_variables.md
- CLI Reference: cli/index.md
- Tracing: core/traced.md
- Studio Web Integration: core/studio_web.md
- Services:
- Assets: core/assets.md
- Attachments: core/attachments.md
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.10.23"
version = "2.10.25"
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
13 changes: 8 additions & 5 deletions packages/uipath/src/uipath/_cli/_utils/_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,14 @@ async def may_override_files(

local_version_display = local_code_version if local_code_version else "Not Set"

try:
push_date = datetime.fromisoformat(remote_metadata.last_push_date)
formatted_date = push_date.strftime("%b %d, %Y at %I:%M %p UTC")
except (ValueError, TypeError):
formatted_date = remote_metadata.last_push_date
if not remote_metadata.last_push_date:
formatted_date = "unknown"
else:
try:
push_date = datetime.fromisoformat(remote_metadata.last_push_date)
formatted_date = push_date.strftime("%b %d, %Y at %I:%M %p UTC")
except (ValueError, TypeError):
formatted_date = remote_metadata.last_push_date

console = ConsoleLogger()
console.warning("Your local version is behind the remote version.")
Expand Down
4 changes: 2 additions & 2 deletions packages/uipath/src/uipath/_cli/_utils/_studio_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -476,8 +476,8 @@ class StudioProjectMetadata(BaseModel):
)

schema_version: int = Field(alias="schemaVersion")
last_push_date: str = Field(alias="lastPushDate")
last_push_author: str = Field(alias="lastPushAuthor")
last_push_date: str | None = Field(alias="lastPushDate", default=None)
last_push_author: str | None = Field(alias="lastPushAuthor", default=None)
code_version: str = Field(alias="codeVersion")


Expand Down
Loading
Loading