feat(cli): stream job logs and result to the handler over uipath-ipc - #1883
feat(cli): stream job logs and result to the handler over uipath-ipc#1883eduard-dumitru wants to merge 2 commits into
Conversation
The pooled `uipath server` now grabs the handler's IIpcLogSink callback during Register (via the injected Message) and points uipath-runtime's process-global log sink at it, so a pooled job's logs stream back over the existing pipe. Best-effort and version-guarded: an older uipath-runtime without the pooled sink API is a no-op and jobs keep the file path. Register(self, message) gains the reach-back handle; the sink forwards each SendLog onto the server loop from the job's worker thread. Bump 2.14.10 -> 2.14.11. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
The pooled log forwarding path is documented as best-effort but currently can surface exceptions during callback acquisition/forwarding, and the new test includes a timing-based sleep that can be flaky.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR adds the “pooled” half of IPC log streaming by wiring a pooled job’s process-global runtime log sink to an IIpcLogSink callback provided by the handler over the existing uipath-ipc connection. The Register handshake is extended to receive an injected Message (reach-back handle) so the server can obtain the callback and forward logs from worker threads onto the server event loop.
Changes:
- Extend the IPC contract so
Register(self, message)receives the injecteduipath_ipc.Messageand uses it to wire a pooled log sink callback. - Add server-side forwarding logic (
run_coroutine_threadsafe+ drain) and lifecycle cleanup to unset the process-global sink when the IPC server exits. - Add tests that lock in the callback seam behavior and bump package version to
2.14.11.
File summaries
| File | Description |
|---|---|
| packages/uipath/src/uipath/_cli/cli_server_ipc.py | Add Message-aware Register, wire pooled log sink to handler callback, and unset sink on server shutdown. |
| packages/uipath/tests/cli/test_server_ipc.py | Update contract test for new Register signature and add pooled log sink seam tests. |
| packages/uipath/pyproject.toml | Bump uipath version from 2.14.10 to 2.14.11. |
Review details
- Files reviewed: 3/3 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| sink_proxy = client.get_callback(IIpcLogSink) | ||
| loop = asyncio.get_running_loop() | ||
|
|
||
| def _forward(job_id: str, log: object) -> None: | ||
| # Runs on the job's worker thread: hand the one-way SendLog to the server loop and return at | ||
| # once. A dropped log (pipe down) must never surface into the job, so failures are swallowed. | ||
| future = asyncio.run_coroutine_threadsafe(sink_proxy.SendLog(job_id, log), loop) | ||
| future.add_done_callback(_drain) |
| sink = captured["sink"] | ||
| log = {"Message": "hello", "LogLevel": 2} | ||
| sink("job-key-42", log) | ||
| await asyncio.sleep(0.05) | ||
| assert sent == [("job-key-42", log)] |
8b4869d to
1dc7057
Compare
343fa8b to
e9af2ca
Compare
uipath-python owns the uipath-ipc contract and connection (_job_api.py) and points uipath-runtime's in-memory sinks at it. The contract is IJobInvocationCommonApi (SendLog + SetResult), the runtime-agnostic base the handler hosts; the JS-only telemetry channel lives on a .NET superset Python doesn't use, so Python keys on the common base directly. Pooled: `uipath server` grabs the handler's IJobInvocationCommonApi callback at Register and installs the sinks per job. Non-pooled: `uipath run --handler-ipc-pipe` dials the handler's per-job server. The result envelope travels inline; output arguments go off-heap via a file pointer. Review fixes: - run the non-pooled handler connection on its own loop/thread so the result sink's blocking ack can't deadlock the job's own event loop - install/clear the process-global sinks inside the job core's lock so concurrently-dispatched RunJobs can't corrupt each other's routing - log (not silently swallow) an IPC result-delivery failure - always disconnect the handler IPC on the run's exit path (async context manager) - register the default runtime factory in start_ipc_server - pin the DTO wire key sets and the log-level mapping in tests Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
e9af2ca to
bea284f
Compare
What
uipath-python owns the uipath-ipc contract and connection (
_job_api.py) and points uipath-runtime's in-memory sinks (from #167) at the handler, so a Python serverless job streams its logs and result back over CoreIPC instead of the files. Covers both transport roles. Version2.14.10 → 2.14.11.Two roles, one contract —
IJobInvocationCommonApi(SendLog+SetResult)uipath server) —Register(message)grabs the handler's callback (message.client.get_callback(IJobInvocationCommonApi)) on the existing pooled pipe and installs the runtime sinks for the job. Gated per-job by aStreamOutputOverIpcflag on the run request; jobs run in-process (serialized), so the process-global sinks genuinely reach the callback.uipath run --handler-ipc-pipe) — dials the handler's per-job server (get_proxy) and installs the sinks on a dedicated loop/thread, so the result's blocking ack can't deadlock the job's own event loop.SendLogcarries logs;SetResultcarries a bounded envelope + a pointer to the output-arguments file the runtime spilled (the payload never crosses IPC). Best-effort and version-guarded: a no-op if the runtime lacksoutput_sinksor uipath-ipc is absent, so an older runtime keeps the files.Related PRs — one endeavor (merge in this order)
Three PRs, one endeavor — stream Python serverless job logs + result to the .NET Robot handler over CoreIPC:
Testing
CLI/IPC tests green, incl. the non-pooled result-ack (deadlock) regression + DTO wire-key tests; ruff/format/mypy clean.
Jira
Tracking: ROBO-5981.
🤖 Generated with Claude Code