Skip to content

Commit e69e8a1

Browse files
authored
Merge pull request #261 from Integration-Automation/feat/decision-table-batch
Add DMN-style decision tables
2 parents 53cf582 + b7dfc4a commit e69e8a1

15 files changed

Lines changed: 392 additions & 1 deletion

File tree

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
## Table of Contents
1515

16+
- [What's new (2026-06-20) — DMN-Style Decision Tables](#whats-new-2026-06-20--dmn-style-decision-tables)
1617
- [What's new (2026-06-20) — Saga / Compensating Rollback](#whats-new-2026-06-20--saga--compensating-rollback)
1718
- [What's new (2026-06-20) — JSONPath Querying](#whats-new-2026-06-20--jsonpath-querying)
1819
- [What's new (2026-06-20) — Multi-Channel Webhook Notifications](#whats-new-2026-06-20--multi-channel-webhook-notifications)
@@ -105,6 +106,12 @@
105106

106107
---
107108

109+
## What's new (2026-06-20) — DMN-Style Decision Tables
110+
111+
Externalize branching into reviewable rule tables. Full reference: [`docs/source/Eng/doc/new_features/v53_features_doc.rst`](docs/source/Eng/doc/new_features/v53_features_doc.rst).
112+
113+
- **`evaluate_table` / `DecisionTable`** (`AC_decision_table`, `ac_decision_table`): replaces nested `AC_if_var` chains with rows of `conditions -> outputs` and a hit policy (`UNIQUE`/`FIRST`/`PRIORITY`/`COLLECT`). Cell conditions are wildcard / literal / `{op, value}` using the executor's standard comparators (reused, not duplicated). Pure-stdlib, fully testable; the DMN way to keep business rules data-driven.
114+
108115
## What's new (2026-06-20) — Saga / Compensating Rollback
109116

110117
Undo completed steps when a later one fails. Full reference: [`docs/source/Eng/doc/new_features/v52_features_doc.rst`](docs/source/Eng/doc/new_features/v52_features_doc.rst).

README/README_zh-CN.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
## 目录
1414

15+
- [本次更新 (2026-06-20) — DMN 式决策表](#本次更新-2026-06-20--dmn-式决策表)
1516
- [本次更新 (2026-06-20) — Saga / 补偿回滚](#本次更新-2026-06-20--saga--补偿回滚)
1617
- [本次更新 (2026-06-20) — JSONPath 查询](#本次更新-2026-06-20--jsonpath-查询)
1718
- [本次更新 (2026-06-20) — 多通道 Webhook 通知](#本次更新-2026-06-20--多通道-webhook-通知)
@@ -104,6 +105,12 @@
104105

105106
---
106107

108+
## 本次更新 (2026-06-20) — DMN 式决策表
109+
110+
将分支外部化为可审查的规则表。完整参考:[`docs/source/Zh/doc/new_features/v53_features_doc.rst`](../docs/source/Zh/doc/new_features/v53_features_doc.rst)
111+
112+
- **`evaluate_table` / `DecisionTable`**(`AC_decision_table``ac_decision_table`):以一列列的 `conditions -> outputs` 加命中政策(`UNIQUE`/`FIRST`/`PRIORITY`/`COLLECT`)取代嵌套 `AC_if_var` 链。单元格条件为通配符 / 字面值 / `{op, value}`,使用执行器标准比较子(重用,不重复)。纯标准库、可完整测试;DMN 让业务规则数据驱动的方式。
113+
107114
## 本次更新 (2026-06-20) — Saga / 补偿回滚
108115

109116
后续步骤失败时回滚已完成步骤。完整参考:[`docs/source/Zh/doc/new_features/v52_features_doc.rst`](../docs/source/Zh/doc/new_features/v52_features_doc.rst)

README/README_zh-TW.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
## 目錄
1414

15+
- [本次更新 (2026-06-20) — DMN 式決策表](#本次更新-2026-06-20--dmn-式決策表)
1516
- [本次更新 (2026-06-20) — Saga / 補償回溯](#本次更新-2026-06-20--saga--補償回溯)
1617
- [本次更新 (2026-06-20) — JSONPath 查詢](#本次更新-2026-06-20--jsonpath-查詢)
1718
- [本次更新 (2026-06-20) — 多通道 Webhook 通知](#本次更新-2026-06-20--多通道-webhook-通知)
@@ -104,6 +105,12 @@
104105

105106
---
106107

108+
## 本次更新 (2026-06-20) — DMN 式決策表
109+
110+
將分支外部化為可審查的規則表。完整參考:[`docs/source/Zh/doc/new_features/v53_features_doc.rst`](../docs/source/Zh/doc/new_features/v53_features_doc.rst)
111+
112+
- **`evaluate_table` / `DecisionTable`**(`AC_decision_table``ac_decision_table`):以一列列的 `conditions -> outputs` 加命中政策(`UNIQUE`/`FIRST`/`PRIORITY`/`COLLECT`)取代巢狀 `AC_if_var` 鏈。儲存格條件為萬用字元 / 字面值 / `{op, value}`,使用執行器標準比較子(重用,不重複)。純標準函式庫、可完整測試;DMN 讓商業規則資料驅動的方式。
113+
107114
## 本次更新 (2026-06-20) — Saga / 補償回溯
108115

109116
後續步驟失敗時復原已完成步驟。完整參考:[`docs/source/Zh/doc/new_features/v52_features_doc.rst`](../docs/source/Zh/doc/new_features/v52_features_doc.rst)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
DMN-Style Decision Tables
2+
=========================
3+
4+
Nested ``AC_if_var`` chains get unreadable fast. A decision table externalizes
5+
branching into rows of ``conditions -> outputs`` evaluated by a **hit policy** —
6+
the DMN way to keep business rules data-driven and reviewable.
7+
8+
Each cell condition is a wildcard (``None`` / ``"-"`` / ``"*"``), a literal
9+
(equality), or ``{"op": "ge", "value": 18}`` using the project's standard
10+
comparators (``eq/ne/lt/le/gt/ge/contains/startswith/endswith`` — reused from the
11+
executor, not duplicated). Pure standard library; imports no ``PySide6``.
12+
13+
Hit policies
14+
------------
15+
16+
================ ===================================================
17+
Policy Behavior
18+
================ ===================================================
19+
``UNIQUE`` Exactly one rule may match (raises if more do).
20+
``FIRST`` The first matching rule wins.
21+
``PRIORITY`` Same as FIRST — first match in rule order.
22+
``COLLECT`` All matching rules' outputs (a list).
23+
================ ===================================================
24+
25+
Headless API
26+
------------
27+
28+
.. code-block:: python
29+
30+
from je_auto_control import evaluate_table
31+
32+
spec = {
33+
"inputs": ["age", "country"],
34+
"hit_policy": "FIRST",
35+
"rules": [
36+
{"conditions": {"age": {"op": "lt", "value": 18}},
37+
"outputs": {"tier": "minor"}},
38+
{"conditions": {"age": {"op": "ge", "value": 18}, "country": "US"},
39+
"outputs": {"tier": "us-adult"}},
40+
{"conditions": {"age": {"op": "ge", "value": 18}},
41+
"outputs": {"tier": "adult"}},
42+
],
43+
}
44+
evaluate_table(spec, {"age": 30, "country": "DE"}) # -> {"tier": "adult"}
45+
46+
``evaluate_table`` returns the matched outputs dict (or ``{}`` if none) for
47+
single-hit policies, and a list for ``COLLECT``. The ``DecisionTable`` class
48+
(``from_dict`` / ``evaluate``) is available for reuse.
49+
50+
Executor command
51+
----------------
52+
53+
``AC_decision_table`` takes ``spec`` and ``context`` (each a dict or JSON
54+
string) and returns ``{result}``. The same operation is exposed as the MCP tool
55+
``ac_decision_table`` and as a Script Builder command under **Flow**.

docs/source/Eng/eng_index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ Comprehensive guides for all AutoControl features.
7575
doc/new_features/v50_features_doc
7676
doc/new_features/v51_features_doc
7777
doc/new_features/v52_features_doc
78+
doc/new_features/v53_features_doc
7879
doc/ocr_backends/ocr_backends_doc
7980
doc/observability/observability_doc
8081
doc/operations_layer/operations_layer_doc
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
DMN 式決策表
2+
============
3+
4+
巢狀的 ``AC_if_var`` 鏈很快就難以閱讀。決策表將分支外部化為一列列的 ``conditions ->
5+
outputs``,並以**命中政策(hit policy)**評估 —— 這是 DMN 讓商業規則維持資料驅動且易於審
6+
查的方式。
7+
8+
每個儲存格條件為萬用字元(``None`` / ``"-"`` / ``"*"``)、字面值(相等)或 ``{"op": "ge",
9+
"value": 18}``,使用本專案標準比較子(``eq/ne/lt/le/gt/ge/contains/startswith/endswith``
10+
—— 重用自執行器,不重複)。純標準函式庫;不匯入 ``PySide6``。
11+
12+
命中政策
13+
--------
14+
15+
================ ===================================================
16+
政策 行為
17+
================ ===================================================
18+
``UNIQUE`` 僅可有一條規則命中(多於一條則拋例外)。
19+
``FIRST`` 第一條命中的規則勝出。
20+
``PRIORITY`` 同 FIRST —— 依規則順序取第一個命中。
21+
``COLLECT`` 所有命中規則的 outputs(清單)。
22+
================ ===================================================
23+
24+
無頭 API
25+
--------
26+
27+
.. code-block:: python
28+
29+
from je_auto_control import evaluate_table
30+
31+
spec = {
32+
"inputs": ["age", "country"],
33+
"hit_policy": "FIRST",
34+
"rules": [
35+
{"conditions": {"age": {"op": "lt", "value": 18}},
36+
"outputs": {"tier": "minor"}},
37+
{"conditions": {"age": {"op": "ge", "value": 18}, "country": "US"},
38+
"outputs": {"tier": "us-adult"}},
39+
{"conditions": {"age": {"op": "ge", "value": 18}},
40+
"outputs": {"tier": "adult"}},
41+
],
42+
}
43+
evaluate_table(spec, {"age": 30, "country": "DE"}) # -> {"tier": "adult"}
44+
45+
``evaluate_table`` 對單一命中政策回傳命中的 outputs dict(若無則 ``{}``),``COLLECT`` 則
46+
回傳清單。``DecisionTable`` 類別(``from_dict`` / ``evaluate``)亦可重用。
47+
48+
執行器指令
49+
----------
50+
51+
``AC_decision_table`` 接受 ``spec`` 與 ``context``(各為 dict 或 JSON 字串),回傳
52+
``{result}``。相同操作亦提供為 MCP 工具 ``ac_decision_table``,以及 Script Builder 中
53+
**Flow** 分類下的指令。

docs/source/Zh/zh_index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ AutoControl 所有功能的完整使用指南。
7575
doc/new_features/v50_features_doc
7676
doc/new_features/v51_features_doc
7777
doc/new_features/v52_features_doc
78+
doc/new_features/v53_features_doc
7879
doc/ocr_backends/ocr_backends_doc
7980
doc/observability/observability_doc
8081
doc/operations_layer/operations_layer_doc

je_auto_control/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,10 @@
282282
)
283283
# Saga orchestrator: multi-step flow with compensating rollback
284284
from je_auto_control.utils.saga import Saga, SagaResult, run_saga
285+
# DMN-style decision tables (rules + hit policy)
286+
from je_auto_control.utils.decision_table import (
287+
DecisionTable, Rule, evaluate_table,
288+
)
285289
# Background popup/interrupt watchdog (unattended automation)
286290
from je_auto_control.utils.watchdog import (
287291
PopupWatchdog, WatchdogRule, default_popup_watchdog,
@@ -746,6 +750,7 @@ def start_autocontrol_gui(*args, **kwargs):
746750
"WebhookChannel", "WebhookResult", "notify_webhook", "set_default_poster",
747751
"json_extract", "json_query", "json_query_one",
748752
"Saga", "SagaResult", "run_saga",
753+
"DecisionTable", "Rule", "evaluate_table",
749754
# MCP server
750755
"AuditLogger", "HttpMCPServer", "MCPContent", "MCPPrompt",
751756
"MCPPromptArgument", "MCPResource", "MCPServer", "MCPTool",

je_auto_control/gui/script_builder/command_schema.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,17 @@ def _add_misc_specs(specs: List[CommandSpec]) -> None:
11541154
),
11551155
description="Run steps; on failure undo completed steps LIFO.",
11561156
))
1157+
specs.append(CommandSpec(
1158+
"AC_decision_table", "Flow", "Decision Table (DMN)",
1159+
fields=(
1160+
FieldSpec("spec", FieldType.STRING,
1161+
placeholder='{"inputs": ["age"], "hit_policy": "FIRST", '
1162+
'"rules": [...]}'),
1163+
FieldSpec("context", FieldType.STRING,
1164+
placeholder='{"age": 30}'),
1165+
),
1166+
description="Evaluate inputs against a rule table (hit policy).",
1167+
))
11571168
specs.append(CommandSpec(
11581169
"AC_generate_sop", "Report", "Generate SOP Document",
11591170
fields=(
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"""DMN-style decision tables: rules + hit policy for externalized branching."""
2+
from je_auto_control.utils.decision_table.decision_table import (
3+
DecisionTable, Rule, evaluate_table,
4+
)
5+
6+
__all__ = ["DecisionTable", "Rule", "evaluate_table"]

0 commit comments

Comments
 (0)