Skip to content

feat(cli): serve the pre-warmed runtime over uipath-ipc alongside HTTP [ROBO-5779] - #1809

Open
eduard-dumitru wants to merge 14 commits into
mainfrom
feat/ROBO-5779-server-ipc-transport
Open

feat(cli): serve the pre-warmed runtime over uipath-ipc alongside HTTP [ROBO-5779]#1809
eduard-dumitru wants to merge 14 commits into
mainfrom
feat/ROBO-5779-server-ipc-transport

Conversation

@eduard-dumitru

@eduard-dumitru eduard-dumitru commented Jul 13, 2026

Copy link
Copy Markdown

Adds a uipath-ipc named-pipe transport to uipath server, served alongside the existing HTTP-over-socket path (ROBO-5779).

What

  • uipath server serves both channels concurrently: HTTP (aiohttp over a Unix socket, or TCP on Windows) and — when --server-socket is given — a uipath-ipc named pipe. HTTP is never torn down; the IPC pipe name is the socket's basename, so the executor spawns the server the same way regardless of channel.
  • IPC hosts IPythonRuntimeServer (StartJob / StopJob) via NamedPipeServerTransport; both edges run jobs through one env/cwd-isolated core (_run_command_isolated).
  • Contract DTOs are typed dataclasses (PythonRunRequest / PythonRunResult) mirroring the .NET side. StopJob returns a bool so CoreIPC treats it as request/response, not fire-and-forget — cancellation itself is a no-op for now (Phase 1).
  • Adds uipath-ipc>=2.5.1 (internal Azure Artifacts feed for now — see the pyproject note; a public-PyPI uipath-ipc release is a prerequisite for a public pip install uipath).
  • Tests: test_server_ipc.py (IPC server) and test_server_transport.py (channel composition / _run_server arg resolution / CLI wiring).

Notes

  • Which transport a given job uses is decided on the .NET Handler side (the PythonRuntimePreferIpc FPS feature flag, Low-Code only) — separate PR in UiPath/hdens.
  • Rebased onto current main; pending green CI (cross-tests + Sonar) before ready.

🤖 Generated with Claude Code

@github-actions github-actions Bot added test:uipath-langchain Triggers tests in the uipath-langchain-python repository test:uipath-runtime test:uipath-integrations labels Jul 13, 2026
@eduard-dumitru
eduard-dumitru force-pushed the feat/ROBO-5779-server-ipc-transport branch from 72b8383 to 952d047 Compare July 21, 2026 11:08
@eduard-dumitru eduard-dumitru changed the title feat(cli): add opt-in uipath-ipc transport to uipath server [ROBO-5779] feat(cli): serve the pre-warmed runtime over uipath-ipc alongside HTTP [ROBO-5779] Jul 21, 2026
@eduard-dumitru
eduard-dumitru force-pushed the feat/ROBO-5779-server-ipc-transport branch 3 times, most recently from c3a6d7d to 7e8f4fb Compare July 23, 2026 08:49
@eduard-dumitru
eduard-dumitru marked this pull request as ready for review July 23, 2026 08:58
Copilot AI review requested due to automatic review settings July 23, 2026 08:58
@github-actions

Copy link
Copy Markdown

🚨 Heads up: uipath-langchain cross-tests are FAILING 🚨

Your changes may break the uipath-langchain-python integration.

⚠️ These checks are NOT enforced by branch protection rules. Please review the failures before merging.

🔍 Inspect the failed run →

@eduard-dumitru
eduard-dumitru force-pushed the feat/ROBO-5779-server-ipc-transport branch from 7e8f4fb to f8eff08 Compare July 23, 2026 09:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a uipath-ipc named-pipe transport to uipath server, served concurrently with the existing HTTP transport, and refactors shared job execution into a single core path used by both transports.

Changes:

  • Introduces an IPC contract/service (IPythonRuntimeServer, DTOs, start_ipc_server) and runs it alongside the HTTP server when --server-socket is provided.
  • Extracts shared job execution into _run_command_isolated and routes HTTP /start through it.
  • Adds unit/integration tests covering transport composition, IPC behavior, and job-core error branches; updates packaging/sonar config for the new IPC module.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
sonar-project.properties Suppresses Sonar naming rules for PascalCase IPC contract/DTOs in cli_server_ipc.py only.
packages/uipath/uv.lock Bumps uipath version and locks uipath-ipc dependency from internal feed.
packages/uipath/tests/cli/test_server_transport.py Verifies _serve composes HTTP+IPC correctly and CLI wires args through.
packages/uipath/tests/cli/test_server_job_core.py Unit tests for _run_command_isolated error/edge branches.
packages/uipath/tests/cli/test_server_ipc.py IPC server tests (StartJob/StopJob + env isolation).
packages/uipath/src/uipath/_cli/cli_server.py Adds IPC integration, shared job core, and updates HTTP handler to use it.
packages/uipath/src/uipath/_cli/cli_server_ipc.py New IPC contract/DTOs/service and server bootstrap.
packages/uipath/pyproject.toml Adds uipath-ipc to project dependencies and configures uv index pinning.
Comments suppressed due to low confidence (1)

packages/uipath/src/uipath/_cli/cli_server.py:292

  • When _run_command_isolated reports a request-shaped failure (e.g., invalid working directory), the HTTP response should keep a 4xx status instead of always defaulting to 200 for any nonzero ExitCode.
    return web.json_response(
        {"success": False, "job_key": job_key, "error": result["Error"]}
    )

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/uipath/src/uipath/_cli/cli_server.py Outdated
Comment thread packages/uipath/src/uipath/_cli/cli_server_ipc.py Outdated
Comment thread packages/uipath/pyproject.toml Outdated
Comment thread packages/uipath/tests/cli/test_server_ipc.py Outdated

@radu-mocanu radu-mocanu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: public install is broken

Please remove uipath-ipc from the required uipath dependencies, or publish it to public PyPI before merging. The PR note says the package currently resolves only from the internal Azure feed. That uv configuration is not included in the published wheel, so public pip install uipath cannot resolve this required dependency.

Also bound the runtime dependency’s minor version when it is released publicly, for example uipath-ipc>=2.5.1,<2.6.0, to protect consumers from incompatible minor releases.

eduard-dumitru and others added 6 commits July 25, 2026 11:52
…P [ROBO-5779]

`uipath server` serves an HTTP channel and, when --server-socket is given, a
uipath-ipc named-pipe channel concurrently — both behind one env/cwd-isolated job
core. Adds the IPythonRuntimeServer contract (StartJob/StopJob) with typed
PythonRunRequest/PythonRunResult dataclasses mirroring the .NET side, plus tests.
Adds uipath-ipc as a dependency.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…OBO-5779]

Move the wire-dictated PascalCase surface (PythonRunRequest/PythonRunResult,
IPythonRuntimeServer, PythonRuntimeService, start_ipc_server) out of cli_server
into cli_server_ipc, re-exported for backward compatibility. Scopes the Sonar
S100/S116 suppression to that single file so the rest of the tree stays strict.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cover the missing/empty-Command rejection and the StopJob bool ack over the
uipath-ipc channel, taking cli_server_ipc to full new-code coverage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Sonar: use resourceKey (not resourcePath) for the multicriteria issue ignore.
- version: bump uipath 2.13.13 -> 2.13.14 (2.13.13 already on PyPI).
- mypy: parametrize dict annotations in the server tests; ignore [type-abstract]
  on get_proxy(IPythonRuntimeServer) (proxying an interface is intended).
- ruff: wrap over-long lines to the 88-col format.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add direct unit tests for _run_command_isolated's error paths (uninitialized
state, bad working directory, SystemExit exit-code mapping, unexpected
exception), lifting new-code coverage over the SonarCloud 90% gate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- HTTP: request-shaped errors (bad working directory) return 400 again,
  not 200 — the shared job core flags them (ClientError) and handle_start
  maps that to 4xx; IPC keeps returning ExitCode/Error.
- PythonRunRequest.Args typed str | list[str] | None to match parse_args and
  all callers (the .NET peer sends a string; HTTP/tests may send a list); add
  parse_args tests covering the string (shlex) path production uses.
- test_server_ipc: replace the fixed time.sleep(0.5) startup delay with a
  bounded readiness poll (connect + no-op StopJob until the pipe answers).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 9 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

packages/uipath/src/uipath/_cli/cli_server.py:265

  • environmentVariables is coerced with ... or {} and then validated with if env_vars and not isinstance(env_vars, dict), which lets falsy non-dict values (e.g. [], "") silently pass as {}. This can mask malformed requests instead of returning 400.
    env_vars = get_field(message, "environmentVariables", "EnvironmentVariables") or {}
    working_dir = get_field(message, "workingDirectory", "WorkingDirectory")

    if env_vars and not isinstance(env_vars, dict):
        return web.json_response(

Comment on lines +154 to +157
os.environ.clear()
os.environ.update(_state.baseline_env)
if isinstance(env_vars, dict):
os.environ.update(env_vars)

@eduard-dumitru eduard-dumitru Jul 27, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a pre-existing condition despite all of the code moving

Comment thread packages/uipath/src/uipath/_cli/cli_server.py Outdated
Comment thread packages/uipath/src/uipath/_cli/cli_server_ipc.py Outdated
Comment thread packages/uipath/pyproject.toml Outdated
@eduard-dumitru

Copy link
Copy Markdown
Author

Blocking: public install is broken

Please remove uipath-ipc from the required uipath dependencies, or publish it to public PyPI before merging. The PR note says the package currently resolves only from the internal Azure feed. That uv configuration is not included in the published wheel, so public pip install uipath cannot resolve this required dependency.

Also bound the runtime dependency’s minor version when it is released publicly, for example uipath-ipc>=2.5.1,<2.6.0, to protect consumers from incompatible minor releases.

✅Done

…[ROBO-5779]

Move the transport-agnostic job core (server state, command table, arg
parsing, and the isolated command runner) out of cli_server into a new
_server_core module. Both the HTTP (cli_server) and uipath-ipc
(cli_server_ipc) transports now import it at module top level, so
cli_server_ipc no longer needs its function-local import of cli_server and
the two modules no longer form an import cycle.

Also switch the Windows branch of _run_server to
asyncio.Runner(loop_factory=ProactorEventLoop): it runs the server on the
Proactor loop without mutating the thread's current-loop slot and tears
tasks down cleanly on exit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…OBO-5779]

The previous commit moved COMMANDS, _state, parse_args and
_run_command_isolated into _server_core, but the tests still reached for
them through cli_server. With no_implicit_reexport enabled, mypy rejects
that (attr-defined). Point each test at the owning module instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 10 changed files in this pull request and generated 2 comments.

Comment thread packages/uipath/src/uipath/_cli/cli_server.py Outdated
Comment thread packages/uipath/src/uipath/_cli/cli_server.py Outdated
eduard-dumitru and others added 2 commits July 27, 2026 14:17
Decouple the uipath-ipc channel from --server-socket. Previously the presence
of --server-socket both set the HTTP socket and implicitly enabled IPC (pipe
name = its basename), overloading one flag with three meanings and surprising
consumers of `uipath server` that only wanted to control the HTTP socket.

--server-socket now means only "the HTTP socket." IPC is served only when
--ipc-pipe names a pipe, on that name verbatim (both sides agree on it out of
band; no basename derivation). Omitting --ipc-pipe is HTTP-only, silently. The
.NET Handler must pass --ipc-pipe to keep serving IPC (paired change).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ict [ROBO-5779]

Preserve the raw environmentVariables value and validate it whenever the field
is present and not a dict, then default to {}. Previously `... or {}` coerced
falsy non-dicts ([], "", 0) to {} before the isinstance check ran, so those
values were silently accepted instead of returning 400.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
eduard-dumitru and others added 3 commits July 28, 2026 10:34
…cy [ROBO-5779]

Drop uipath-ipc from the required runtime dependencies. It is now imported
lazily inside start_ipc_server, so `pip install uipath` no longer pulls it and
importing cli_server (HTTP-only serving) never touches it. The DTOs and the
IPythonRuntimeServer contract are pure stdlib and don't reference it.

If --ipc-pipe is requested but uipath-ipc is not installed, fail fast with a
clear message rather than silently degrading — the .NET Handler only passes
--ipc-pipe when the venv is IPC-capable, so the mismatch is a broken image that
should halt loudly. Prebaked venvs that ship uipath-ipc get the IPC channel as
before.

uipath-ipc moves to the dev dependency group (tests + mypy still need it); the
lock is regenerated accordingly. This also resolves the public-install concern
at its root: uipath no longer requires uipath-ipc at all.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow up the previous change (uipath-ipc dropped from required deps) by
declaring it honestly as an optional dependency: `pip install uipath[ipc]`.
This documents the feature and hints the supported version range
(>=2.5.1,<2.6.0) without making it a hard dependency — base `pip install
uipath` stays free of it, so the public-install concern remains resolved.

Runtime behaviour is unchanged: uipath-ipc is still imported lazily in
start_ipc_server, and --ipc-pipe without it fails fast (the error now points at
`uipath[ipc]`). It stays in the dev group so tests + mypy still install it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…5779]

Hoist _unique_pipe() and the coroutine creation out of the pytest.raises block
so only asyncio.run(coro) can raise inside it (SonarCloud S5778). This makes the
test assert that start_ipc_server is what fails, not an incidental setup call.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread packages/uipath/src/uipath/_cli/cli_server_ipc.py Outdated
@eduard-dumitru
eduard-dumitru force-pushed the feat/ROBO-5779-server-ipc-transport branch from bf9c244 to 5d69c8c Compare July 28, 2026 12:25
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test:uipath-integrations test:uipath-langchain Triggers tests in the uipath-langchain-python repository test:uipath-runtime

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants