Skip to content

feat(business-rules): add debug runs to run() - #1917

Open
ashishupadhyay88 wants to merge 13 commits into
feat/business-rules-evaluatefrom
feat/business-rules-run
Open

ashishupadhyay88 wants to merge 13 commits into
feat/business-rules-evaluatefrom
feat/business-rules-run

Conversation

@ashishupadhyay88

@ashishupadhyay88 ashishupadhyay88 commented Sep 29, 2026 •

Copy link
Copy Markdown

Summary

Jira: MST-15365

PR 2 of 2 for the Business Rules service. Stacked on #1912, so review that one first.

This PR adds debug runs: running an undeployed rule read straight from a Studio project. They use the same run() / run_async() with an optional debug=DebugRunContext(...). The rule name stays the first parameter, so businessRule binding overrides apply to debug runs too.

from uipath.platform import UiPath
from uipath.platform.business_rules import DebugRunContext

sdk = UiPath()

# By project: nothing else required
sdk.business_rules.run("Business Rules", {"age": 78},
                       debug=DebugRunContext(project_id="041e6279-…", file_name="Business rule.dmn"))

# By job lineage: the service finds the project from the running debug job
sdk.business_rules.run("Business Rules", {"age": 78},
                       debug=DebugRunContext(job_key="2c805370-…"),
                       organization_unit_id=3221137,          # the job's numeric folder id
                       explain=True, folder_path="Shared/Solution_ashish1")

Flow, all cases

flowchart TD
    A["run(name, input) / run_async(name, input)"] --> O{"businessRule binding<br/>override for name?"}
    O -- yes --> O1["name and folder replaced"] --> V
    O -- no --> V{"name safe, input a mapping<br/>with at most 256 keys?"}
    V -- no --> X["ValueError, nothing sent"]
    V -- yes --> F{"folder: folder_key / folder_path (looked up)<br/>/ organization_unit_id / env"}
    F -- "folder_key and folder_path" --> X
    F --> EX{"explain=True and<br/>no folder key?"}
    EX -- yes --> X
    EX -- no --> M{"debug?"}
    M -- "no: deployed" --> D1{"folder key or<br/>organization_unit_id?"}
    D1 -- no --> X
    D1 -- yes --> EV["POST …/v1/business-rules/evaluate<br/>folderkey / organizationunitid as given<br/>businessRuleName, version?"]
    M -- yes --> H{"debug.project_id?"}
    H -- yes --> DP["POST …/debug/evaluate<br/>projectId, fileName?, version? (no businessRuleName)<br/>folderkey if known; jobkey / organizationunitid only if passed"]
    H -- "no: job lineage" --> J{"job_key or UIPATH_JOB_KEY,<br/>and organization_unit_id?"}
    J -- missing --> X
    J -- yes --> DR["POST …/debug/evaluate<br/>businessRuleName, fileName?, version?<br/>x-uipath-jobkey, x-uipath-organizationunitid<br/>folderkey if known"]
    EV --> T{"trace_context?"}
    DP --> T
    DR --> T
    T -- yes --> T1["traceparent = explicit"]
    T -- no --> T2["traceparent = UIPATH_TRACE_ID / current span"]
    T1 --> BS["BaseService: bearer token, /org/tenant URL, retries"]
    T2 --> BS
    BS --> R{"HTTP status"}
    R -- "4xx / 5xx" --> E["EnrichedException"]
    R -- "200 / 207" --> S["result for input-1 → status + mode<br/>Deployed: business_rule_name, version<br/>Debug: project_id, file_name"]
Loading

What changes

Before (#1912) After
run(name, input, …) deployed only adds debug: Optional[DebugRunContext]; None means the deployed rule
DebugRunContext(project_id, file_name, job_key) none new; the rule is always name, and the job's numeric folder id is run()'s organization_unit_id
RunMode DEPLOYED adds DEBUG
BusinessRuleRunResult deployed fields adds project_id, file_name for debug runs

Behaviour

The SDK checks only what each mode requires. Optional values the caller passes are sent as given.

Required Sent when given
Project mode (debug.project_id) only project_id; no folder, job key or numeric id file_name, version, decision_names, folder_key / folder_path, job_key, organization_unit_id
Job-lineage mode (no project_id) job_key (or UIPATH_JOB_KEY) and organization_unit_id file_name, version, decision_names, folder_key / folder_path
explain=True, either mode a folder key (folder_key, or folder_path looked up, or env) –
  • The body names the project one way only. It carries projectId in project mode and businessRuleName in job-lineage mode, never both, because the service documents them as alternatives.
  • UIPATH_JOB_KEY is used only where it's required, in job-lineage mode. It's never added on its own in project mode.
  • version is sent when given. The debug endpoint has no version field, so on alpha it was accepted and ignored.
  • The account id comes from the gateway, so the SDK never sends x-uipath-internal-accountid.
  • folder_key + folder_path together are still a ValueError, as elsewhere in the SDK.
  • Overrides and trace: businessRule overrides and trace_context from feat(business-rules): add BusinessRulesService with run() #1912 apply to debug runs too.

For reviewers

  • Retries on debug runs. Debug runs use the shared BaseService retry. The .NET client doesn't retry debug runs. A plain 500 isn't retried in either SDK.
  • Version bump. uipath-platform goes from 0.2.33 to 0.2.34. uv lock --check passes.
  • Up to date with feat(business-rules): add BusinessRulesService with run() #1912, which includes main.

Test plan

  • 69 tests in the file, 16 more than feat(business-rules): add BusinessRulesService with run() #1912. They cover:
    • debug by project and by job lineage, each sending the right fields and headers
    • project mode needing no folder, and sending an optional job key / numeric id as given (with no UIPATH_JOB_KEY added)
    • version sent with debug, and job-lineage mode requiring the job key and the numeric id
    • explain needing a folder key in both modes
    • override and explicit trace on debug runs, and async
  • The full uipath-platform suite passes: 1816 passed, 7 skipped (live credentials).
  • ruff check, ruff format --check and mypy src tests are clean in the locked uv environment.
  • Real alpha, from a Coded Function (uip function run): 60 passed, 0 failed, 5 recorded, 1 slow case skipped.
    • evaluate, debug by project and debug by job lineage, each with explain off and on, checked on the wire
    • project mode with no folder, and with an optional job key + numeric id
    • version sent with debug and ignored by the service
    • explain without a folder key → ValueError
    • concurrent run_async calls each keeping their own trace

🤖 Generated with Claude Code

Development Packages

uipath-platform

[project]
dependencies = [
  # Exact version (copy-paste ready):
  "uipath-platform==0.2.35.dev1019177758",

  # Any version from this PR (uncomment to use a range instead):
  # "uipath-platform>=0.2.35.dev1019170000,<0.2.35.dev1019180000",
]

[[tool.uv.index]]
name = "testpypi"
url = "https://test.pypi.org/simple/"
publish-url = "https://test.pypi.org/legacy/"
explicit = true

[tool.uv.sources]
uipath-platform = { index = "testpypi" }

Copilot AI balanced review requested due to automatic review settings September 29, 2026 02:16
@github-actions github-actions Bot added test:uipath-langchain Triggers tests in the uipath-langchain-python repository test:uipath-integrations labels Sep 29, 2026

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.

Copilot review overview

🟡 Changes recommended

Debug selector validation can omit a required header, and the numeric organization-unit API is typed incorrectly.

Review effort: Balanced
Findings: 2 Medium severity

Open (2)
What changed in this PR

Adds unified Business Rules execution for deployed rules and undeployed Studio DMNs.

Changes:

  • Replaces evaluate() with run() and run_async().
  • Adds debug/deployed contexts, run modes, result metadata, and validation.
  • Expands tests and bumps uipath-platform to 0.2.34.
File Description
packages/​uipath/​uv.lock Updates platform package lock version.
packages/​uipath-platform/​uv.lock Updates package lock version.
packages/​uipath-platform/​tests/​services/​test_business_rules_service.py Tests unified and debug runs.
packages/​uipath-platform/​src/​uipath/​platform/​business_rules/​business_rules.py Adds public run models.
packages/​uipath-platform/​src/​uipath/​platform/​business_rules/​_business_rules_service.py Implements run routing and requests.
packages/​uipath-platform/​src/​uipath/​platform/​business_rules/​__init__.py Exports the new API.
packages/​uipath-platform/​pyproject.toml Bumps package version.
packages/​uipath-platform/​CLAUDE.md Updates service documentation.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/uipath-platform/src/uipath/platform/business_rules/business_rules.py Outdated
Adds DebugRunContext, the second run context of run()/run_async(), for
undeployed DMNs read from a Studio project. As in the .NET client's
RunAsync, exactly one of deployed/debug must be set and it selects the
endpoint:

- DeployedRunContext -> /v1/business-rules/evaluate
- DebugRunContext(project_id | rule_name, file_name, job_key,
  organization_unit_id) -> /v1/business-rules/debug/evaluate

A debug run named by rule_name also needs job_key (defaults to
UIPATH_JOB_KEY) and organization_unit_id. Following business-rules#104,
explain=True needs a folder key in both modes, and debug runs send the
key whenever one is known so their spans can be stored.

BusinessRuleRunResult gains project_id and file_name for debug runs, and
RunMode gains DEBUG. Bumps uipath-platform to 0.2.34.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ashishupadhyay88 ashishupadhyay88 changed the title feat(business-rules): add debug runs behind a single run() entry point feat(business-rules): add debug runs to run() Sep 29, 2026
@ashishupadhyay88

Copy link
Copy Markdown
Author

Tested against alpha

This tests debug runs through run() / run_async() with DebugRunContext, on commit baa54e02.

Setup

  • Tenant: alpha.uipath.com/bruleswe/DefaultTenant
  • Login: uipath auth --alpha
  • Folder: Shared/Solution_ashish1 (numeric id 3373422)
  • Studio project: 59bbc3f1-2dc5-4ac6-a951-f52c5fec7fdf
  • Input: {"age": 14}

Debug by project_id

# Call Result
A1 debug=DebugRunContext(project_id=...) ✅ Success, mode: Debug, {"category": "minor"}; service read file_name: "Business rule.dmn" (first .dmn)
A2 + explain=True + folder_path ✅ Success: folder key reaches the debug endpoint, which satisfies business-rules#104
A3 explain=True with no folder ✅ Local ValueError, nothing sent
A4 run_async, same as A1 ✅ Success

Debug by rule_name + job_key

The service works out the project from the job's lineage.

# Call Result
B1 rule_name="Business Rules", no job_key ✅ Local ValueError, nothing sent (the service would return 400)
B2 made-up job_key + organization_unit_id="3373422" ✅ Service did the lookup: 404 "no such job"
C1 real job_key 2085a34c-… + organization_unit_id ✅ Success: resolved to project 59bbc3f1-…, "Business rule.dmn", {"category": "minor"}
C2 C1 + explain=True + folder_path ✅ Success
C3 run_async, same as C2 ✅ Success

What this confirms

  • Routing. A call with no project name went to …/businessrules_/v1/business-rules/debug/evaluate, and it was routed correctly.
  • Headers. x-uipath-jobkey, x-uipath-organizationunitid and x-uipath-folderkey all arrive as the service expects.

Note: in debug mode business_rule_name is null in the result, even when the run is named by rule name. This matches .NET RuleRunResult, and it can be filled in if reviewers prefer.

The full suite passes on this branch: 1779 passed, 7 skipped.

ashishupadhyay88 and others added 3 commits September 29, 2026 09:52
…hecks

Addresses Copilot review on the debug run context:

- DebugRunContext.organization_unit_id is now Optional[int], matching
  the numeric Orchestrator folder id (and Task.organization_unit_id);
  it is converted to text only for the x-uipath-organizationunitid
  header. Numeric strings still validate.
- job_key and organization_unit_id are required only when the run is
  named by rule_name without project_id. With a project_id the service
  uses the project as given and never reads the job's lineage
  (StudioDmnResolver.projectIdOf), so neither is needed.
- Build run contexts outside pytest.raises so each block has a single
  call that can raise (Sonar python:S5778).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…to feat/business-rules-run

# Conflicts:
#	packages/uipath-platform/src/uipath/platform/business_rules/_business_rules_service.py
#	packages/uipath-platform/src/uipath/platform/business_rules/business_rules.py
#	packages/uipath-platform/tests/services/test_business_rules_service.py

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.

Copilot review overview

🟢 Approval recommended

The debug-run implementation is coherent with the documented flow and is covered across validation, request construction, results, tracing, and asynchronous execution.

Review effort: Balanced
Findings: None

Resolved since last review (2)

ashishupadhyay88 and others added 3 commits September 29, 2026 18:37
Brings #1912's review fix (name-first run() with resource overrides)
into the debug PR and reshapes debug runs to match:

- run(name, input, *, debug=DebugRunContext(...)): the rule name is the
  first parameter for both modes, so bindings can remap it for debug
  runs too. DebugRunContext drops rule_name and keeps project_id,
  file_name, job_key and organization_unit_id.
- Without project_id, the run resolves the project from the job's
  lineage and needs job_key (or UIPATH_JOB_KEY) and organization_unit_id.
- version applies to deployed rules only and is rejected with debug.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings #1912's wording change into the debug PR and rewords the debug
docstrings and field descriptions the same way. Literal .dmn file names
in examples and tests are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@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 →

ashishupadhyay88 and others added 2 commits September 29, 2026 19:06
…to feat/business-rules-run

# Conflicts:
#	packages/uipath-platform/src/uipath/platform/business_rules/_business_rules_service.py
The service documents projectId and businessRuleName as alternative ways
to find a debug run's project. Each request now carries exactly one:

- Project mode (debug.project_id): body has projectId (+ fileName), no
  businessRuleName; no x-uipath-jobkey or x-uipath-organizationunitid,
  even when UIPATH_JOB_KEY is set. Setting job_key or
  organization_unit_id with project_id is a ValueError.
- Job-lineage mode (no project_id): body has businessRuleName; the job
  key (or UIPATH_JOB_KEY) and organization_unit_id are required and sent.

The rule name stays run()'s first argument, validated and remappable by
businessRule bindings; it goes on the wire only when it decides which
project runs.

Co-Authored-By: Claude Opus 5.5 <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.

Copilot review overview

🟢 Approval recommended

The implementation matches the stated request contracts and is supported by focused sync and async tests.

Review effort: Balanced
Findings: None

ashishupadhyay88 and others added 4 commits September 30, 2026 09:11
…to feat/business-rules-run

# Conflicts:
#	packages/uipath-platform/src/uipath/platform/business_rules/_business_rules_service.py
… debug too

Brings #1912's organization_unit_id into the debug PR and applies the
"require only what's needed, accept optional values" rule to debug runs:

- organization_unit_id moves from DebugRunContext to run(), next to
  folder_key / folder_path. Job-lineage mode requires it (and a job key,
  explicit or UIPATH_JOB_KEY); project mode needs no folder at all.
- Project mode no longer rejects a job key or folder id the caller
  passes: they're sent as given. UIPATH_JOB_KEY is still not added on its
  own, and businessRuleName is still never sent with projectId.
- version is sent with debug runs when given, instead of being rejected.
- explain=True needs a folder key in every mode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1912 now takes 0.2.34 (main released 0.2.33), so this PR moves to the
next version.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ashishupadhyay88 ashishupadhyay88 added the build:dev Create a dev build from the pr label Sep 30, 2026
@ashishupadhyay88

Copy link
Copy Markdown
Author

Tested in a coded agent on alpha (bruleswe/DefaultTenant)

I built a LangGraph coded agent that calls sdk.business_rules.run_async(...) from two LLM tools, run_business_rule (deployed) and debug_business_rule (debug, by project). It ran against the unreleased SDK from this PR's TestPyPI dev build (uipath-platform==0.2.35.dev1019177758). I deployed it to Orchestrator (personal workspace) and ran it as jobs.

Job Question Tool calls Result
0e3e9b0a… Category for a 78-year-old run_business_rule({"age": 78}) ✅ Deployed · Success · adult · 1.1.5
84c64b3a… Studio version, age 14, explain=true, no folder key passed debug_business_rule({"age": 14}) ✅ Debug · Success · minor · Business rule.dmn
5e4d9370… Compare 14, 35 and 78, explain=true 3 × run_business_rule ✅ Deployed · Success · minor / adult / adult

Local runs before deploying: 5/5 correct with uip codedagent run, and a 3-case smoke eval scored 3/3 (1.0).

What this shows:

  • In a job, the folder comes from the environment. In job 2, explain=true needs a folder key and the agent passed none. The SDK read UIPATH_FOLDER_KEY from the job and the service accepted it.
  • run_async works inside LangGraph, including several calls in one turn.
  • No extra auth setup. The robot token and the gateway-injected account and tenant headers were enough.

Not checked: nesting of the Business Rules span in the agent trace. uip or jobs traces only serves Agent-type processes.

Details: https://claude.ai/artifact/Tgr4kV4qg6T8K35yVGstN3

This branch has not been deployed

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

Labels

build:dev Create a dev build from the pr test:uipath-integrations test:uipath-langchain Triggers tests in the uipath-langchain-python repository

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants