feat(business-rules): add debug runs to run() - #1917
ashishupadhyay88 wants to merge 13 commits into
Conversation
There was a problem hiding this comment.
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
Open (2)
What changed in this PR
Adds unified Business Rules execution for deployed rules and undeployed Studio DMNs.
Changes:
- Replaces
evaluate()withrun()andrun_async(). - Adds debug/deployed contexts, run modes, result metadata, and validation.
- Expands tests and bumps
uipath-platformto 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.
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>
5aec977 to
baa54e0
Compare
Tested against alphaThis tests debug runs through Setup
Debug by
Debug by The service works out the project from the job's lineage.
What this confirms
Note: in debug mode The full suite passes on this branch: 1779 passed, 7 skipped. |
…to feat/business-rules-run
…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
There was a problem hiding this comment.
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)
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>
…to feat/business-rules-run
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>
🚨 Heads up:
|
…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>
…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>
…to feat/business-rules-run
#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>
Tested in a coded agent on alpha (
|
| 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=trueneeds a folder key and the agent passed none. The SDK readUIPATH_FOLDER_KEYfrom the job and the service accepted it. run_asyncworks 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.

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 optionaldebug=DebugRunContext(...). The rule name stays the first parameter, sobusinessRulebinding overrides apply to debug runs too.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"]What changes
run(name, input, …)debug: Optional[DebugRunContext];Nonemeans the deployed ruleDebugRunContext(project_id, file_name, job_key)name, and the job's numeric folder id isrun()'sorganization_unit_idRunModeDEPLOYEDDEBUGBusinessRuleRunResultproject_id,file_namefor debug runsBehaviour
The SDK checks only what each mode requires. Optional values the caller passes are sent as given.
debug.project_id)project_id; no folder, job key or numeric idfile_name,version,decision_names,folder_key/folder_path,job_key,organization_unit_idproject_id)job_key(orUIPATH_JOB_KEY) andorganization_unit_idfile_name,version,decision_names,folder_key/folder_pathexplain=True, either modefolder_key, orfolder_pathlooked up, or env)projectIdin project mode andbusinessRuleNamein job-lineage mode, never both, because the service documents them as alternatives.UIPATH_JOB_KEYis used only where it's required, in job-lineage mode. It's never added on its own in project mode.versionis sent when given. The debug endpoint has no version field, so on alpha it was accepted and ignored.x-uipath-internal-accountid.folder_key+folder_pathtogether are still aValueError, as elsewhere in the SDK.businessRuleoverrides andtrace_contextfrom feat(business-rules): add BusinessRulesService with run() #1912 apply to debug runs too.For reviewers
BaseServiceretry. The .NET client doesn't retry debug runs. A plain 500 isn't retried in either SDK.uipath-platformgoes from 0.2.33 to 0.2.34.uv lock --checkpasses.main.Test plan
UIPATH_JOB_KEYadded)versionsent with debug, and job-lineage mode requiring the job key and the numeric idexplainneeding a folder key in both modesuipath-platformsuite passes: 1816 passed, 7 skipped (live credentials).ruff check,ruff format --checkandmypy src testsare clean in the locked uv environment.uip function run): 60 passed, 0 failed, 5 recorded, 1 slow case skipped.explainoff and on, checked on the wireversionsent with debug and ignored by the serviceexplainwithout a folder key →ValueErrorrun_asynccalls each keeping their own trace🤖 Generated with Claude Code
Development Packages
uipath-platform