pyDMNrules-enhanced 1.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pydmnrules_enhanced-1.5.0/LICENSE +24 -0
- pydmnrules_enhanced-1.5.0/LICENSE_MCP +22 -0
- pydmnrules_enhanced-1.5.0/MANIFEST.in +8 -0
- pydmnrules_enhanced-1.5.0/MCP_PROJECT_SUMMARY.md +399 -0
- pydmnrules_enhanced-1.5.0/PKG-INFO +358 -0
- pydmnrules_enhanced-1.5.0/README.md +329 -0
- pydmnrules_enhanced-1.5.0/README_MCP.md +428 -0
- pydmnrules_enhanced-1.5.0/SETUP_GUIDE.md +277 -0
- pydmnrules_enhanced-1.5.0/claude_desktop_config.json +13 -0
- pydmnrules_enhanced-1.5.0/pyDMNrules/DMNrules.py +6585 -0
- pydmnrules_enhanced-1.5.0/pyDMNrules/__init__.py +2 -0
- pydmnrules_enhanced-1.5.0/pyDMNrules_enhanced.egg-info/PKG-INFO +358 -0
- pydmnrules_enhanced-1.5.0/pyDMNrules_enhanced.egg-info/SOURCES.txt +21 -0
- pydmnrules_enhanced-1.5.0/pyDMNrules_enhanced.egg-info/dependency_links.txt +1 -0
- pydmnrules_enhanced-1.5.0/pyDMNrules_enhanced.egg-info/requires.txt +4 -0
- pydmnrules_enhanced-1.5.0/pyDMNrules_enhanced.egg-info/top_level.txt +2 -0
- pydmnrules_enhanced-1.5.0/pydmnrules_mcp/__init__.py +28 -0
- pydmnrules_enhanced-1.5.0/pydmnrules_mcp/server.py +632 -0
- pydmnrules_enhanced-1.5.0/pyproject.toml +6 -0
- pydmnrules_enhanced-1.5.0/requirements_mcp.txt +14 -0
- pydmnrules_enhanced-1.5.0/setup.cfg +4 -0
- pydmnrules_enhanced-1.5.0/setup.py +23 -0
- pydmnrules_enhanced-1.5.0/tests/test_pyDMNrules.py +2260 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2020 Russell McDonell
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
The name of Russell McDonell shall not used to endorse or promote products
|
16
|
+
derived from this software without specific prior written permission.
|
17
|
+
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
19
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
20
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
21
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
22
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
23
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
24
|
+
SOFTWARE.
|
@@ -0,0 +1,22 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 uengine (rickjang)
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
@@ -0,0 +1,399 @@
|
|
1
|
+
# pyDMNrules MCP Server - 프로젝트 요약
|
2
|
+
|
3
|
+
## 프로젝트 개요
|
4
|
+
|
5
|
+
pyDMNrules 엔진을 사용하여 DMN (Decision Model Notation) XML 파일을 로드하고 의사결정을 실행하는 FastMCP 기반 MCP 서버입니다.
|
6
|
+
|
7
|
+
### 주요 특징
|
8
|
+
|
9
|
+
✅ **DMN XML 지원**: DMN 1.1/1.2/1.3 표준 XML 파일 로드 및 실행
|
10
|
+
✅ **규칙 관리**: 저장, 로드, 삭제, 목록 조회
|
11
|
+
✅ **스키마 자동 추출**: LLM이 이해할 수 있는 입력/출력 스키마 제공
|
12
|
+
✅ **의사결정 추적**: 실행 경로 및 결과에 대한 상세 trace
|
13
|
+
✅ **FastMCP 기반**: 안정적이고 빠른 MCP 서버
|
14
|
+
|
15
|
+
## 프로젝트 구조
|
16
|
+
|
17
|
+
```
|
18
|
+
pyDMNrules/
|
19
|
+
├── pydmnrules_mcp_server.py # MCP 서버 메인 파일 ⭐
|
20
|
+
├── requirements_mcp.txt # 의존성 목록
|
21
|
+
├── README_MCP.md # 사용자 문서
|
22
|
+
├── SETUP_GUIDE.md # 설정 가이드
|
23
|
+
├── MCP_PROJECT_SUMMARY.md # 이 파일
|
24
|
+
├── claude_desktop_config.json # Claude Desktop 설정 예제
|
25
|
+
│
|
26
|
+
├── test_mcp_server.py # 기본 테스트
|
27
|
+
├── test_mcp_with_simulation.py # 완전한 테스트 ⭐
|
28
|
+
├── test_pydmnrules_direct.py # pyDMNrules 직접 테스트
|
29
|
+
├── test_pydmnrules_direct2.py # pyDMNrules 직접 테스트 (수정)
|
30
|
+
│
|
31
|
+
├── rules/ # DMN 규칙 저장 디렉토리
|
32
|
+
│ └── simulation.dmn # 예제 규칙
|
33
|
+
│
|
34
|
+
└── pyDMNrules/ # pyDMNrules 엔진
|
35
|
+
├── __init__.py
|
36
|
+
└── DMNrules.py
|
37
|
+
```
|
38
|
+
|
39
|
+
## 핵심 파일 설명
|
40
|
+
|
41
|
+
### 1. `pydmnrules_mcp_server.py` ⭐
|
42
|
+
|
43
|
+
MCP 서버의 메인 파일입니다. 다음을 포함합니다:
|
44
|
+
|
45
|
+
**클래스**:
|
46
|
+
- `PyDMNrulesEngine`: pyDMNrules 엔진을 감싸는 래퍼
|
47
|
+
- `DMNModel`: DMN 규칙 파일 관리
|
48
|
+
- `DecisionResult`: 의사결정 결과 데이터 모델
|
49
|
+
|
50
|
+
**MCP Tools (7개)**:
|
51
|
+
1. `load_rule` - 규칙 로드
|
52
|
+
2. `save_rule` - 규칙 저장
|
53
|
+
3. `list_rules` - 규칙 목록
|
54
|
+
4. `delete_rule` - 규칙 삭제
|
55
|
+
5. `get_rule_schema` - 스키마 조회 (중요!)
|
56
|
+
6. `infer_decision` - 의사결정 실행
|
57
|
+
7. `check_engine_status` - 엔진 상태
|
58
|
+
|
59
|
+
### 2. `test_mcp_with_simulation.py` ⭐
|
60
|
+
|
61
|
+
완전한 기능 테스트 스크립트입니다. 다음을 테스트합니다:
|
62
|
+
- 규칙 저장/로드
|
63
|
+
- 스키마 조회
|
64
|
+
- 의사결정 실행 (3가지 시나리오)
|
65
|
+
- 결과 검증
|
66
|
+
|
67
|
+
### 3. `requirements_mcp.txt`
|
68
|
+
|
69
|
+
필수 의존성:
|
70
|
+
```
|
71
|
+
fastmcp>=0.4.0
|
72
|
+
pydantic>=2.0.0
|
73
|
+
aiofiles>=23.0.0
|
74
|
+
pydmnrules-enhanced>=1.5.0
|
75
|
+
```
|
76
|
+
|
77
|
+
## 구현 세부사항
|
78
|
+
|
79
|
+
### 핵심 기능
|
80
|
+
|
81
|
+
#### 1. DMN XML 로드
|
82
|
+
```python
|
83
|
+
dmn_model = DMN()
|
84
|
+
status = dmn_model.useXML(xml_content)
|
85
|
+
```
|
86
|
+
|
87
|
+
pyDMNrules의 `useXML()` 메서드를 사용하여 XML 문자열로부터 규칙을 로드합니다.
|
88
|
+
|
89
|
+
#### 2. 메타데이터 추출
|
90
|
+
```python
|
91
|
+
def _extract_metadata(self, dmn_model: DMN, rule_name: str) -> Dict[str, Any]:
|
92
|
+
"""
|
93
|
+
Glossary로부터 Variable 이름 추출
|
94
|
+
Decision Tables 정보 추출
|
95
|
+
입력/출력 필드 정보 구성
|
96
|
+
"""
|
97
|
+
```
|
98
|
+
|
99
|
+
**중요**: pyDMNrules는 XML의 input label을 Variable 이름으로 사용합니다.
|
100
|
+
- XML: `<input label="Season">` → Variable: `"Season"`
|
101
|
+
- XML: `<input label="How many guests">` → Variable: `"How many guests"` (공백 포함!)
|
102
|
+
|
103
|
+
#### 3. 스키마 제공
|
104
|
+
```python
|
105
|
+
{
|
106
|
+
"rule_name": "simulation",
|
107
|
+
"variable_names": ["Season", "How many guests", "Guests with children"],
|
108
|
+
"important_note": "⚠️ Use EXACT variable names...",
|
109
|
+
"inputs": {
|
110
|
+
"Season": {
|
111
|
+
"variable_name": "Season",
|
112
|
+
"feel_name": "Data.Season",
|
113
|
+
"type": "string",
|
114
|
+
"required": true
|
115
|
+
}
|
116
|
+
}
|
117
|
+
}
|
118
|
+
```
|
119
|
+
|
120
|
+
LLM이 `variable_names`를 보고 정확한 키 이름을 사용할 수 있도록 합니다.
|
121
|
+
|
122
|
+
#### 4. 의사결정 실행
|
123
|
+
```python
|
124
|
+
(status, result_data) = dmn_model.decide(input_context)
|
125
|
+
```
|
126
|
+
|
127
|
+
pyDMNrules의 `decide()` 메서드를 호출하여 의사결정을 실행합니다.
|
128
|
+
|
129
|
+
#### 5. 결과 파싱
|
130
|
+
```python
|
131
|
+
def _parse_result(self, result_data: Any) -> Dict[str, Any]:
|
132
|
+
"""
|
133
|
+
단일 decision 또는 여러 decision 처리
|
134
|
+
Result, Executed Rule, Annotations 추출
|
135
|
+
"""
|
136
|
+
```
|
137
|
+
|
138
|
+
### 주요 도전과제 및 해결
|
139
|
+
|
140
|
+
#### 문제 1: Variable 이름 불일치
|
141
|
+
|
142
|
+
**문제**:
|
143
|
+
```python
|
144
|
+
# ✗ 작동하지 않음
|
145
|
+
{"season": "Fall", "guestCount": 4}
|
146
|
+
```
|
147
|
+
|
148
|
+
**원인**: pyDMNrules의 Glossary에는 XML의 정확한 label이 저장됨
|
149
|
+
|
150
|
+
**해결**:
|
151
|
+
```python
|
152
|
+
# ✓ 작동함
|
153
|
+
{"Season": "Fall", "How many guests": 4}
|
154
|
+
```
|
155
|
+
|
156
|
+
스키마에 `variable_names` 리스트를 추가하고, `important_note`로 강조
|
157
|
+
|
158
|
+
#### 문제 2: 결과 구조 다양성
|
159
|
+
|
160
|
+
**문제**: pyDMNrules는 상황에 따라 다른 결과 구조 반환
|
161
|
+
- 단일 decision: `dict`
|
162
|
+
- 여러 decision: `list[dict]`
|
163
|
+
|
164
|
+
**해결**: `_parse_result()`에서 두 가지 경우 모두 처리
|
165
|
+
|
166
|
+
```python
|
167
|
+
if isinstance(result_data, list):
|
168
|
+
return {
|
169
|
+
"final_result": all_results[-1],
|
170
|
+
"all_results": all_results,
|
171
|
+
"decision_count": len(all_results)
|
172
|
+
}
|
173
|
+
```
|
174
|
+
|
175
|
+
#### 문제 3: Trace 정보 구성
|
176
|
+
|
177
|
+
**문제**: pyDMNrules의 결과에서 실행 경로 추출
|
178
|
+
|
179
|
+
**해결**: `_build_trace()`에서 `Executed Rule` 정보 파싱
|
180
|
+
|
181
|
+
```python
|
182
|
+
if 'Executed Rule' in result_data:
|
183
|
+
executed_rule = result_data['Executed Rule']
|
184
|
+
# (description, table_name, rule_id) 튜플 파싱
|
185
|
+
```
|
186
|
+
|
187
|
+
## 테스트 결과
|
188
|
+
|
189
|
+
### simulation.dmn 테스트 (성공 ✅)
|
190
|
+
|
191
|
+
```bash
|
192
|
+
$ python3 test_mcp_with_simulation.py
|
193
|
+
|
194
|
+
테스트 케이스 1: Fall, 4 guests, with children
|
195
|
+
✓ 결과:
|
196
|
+
Dish: Spareribs
|
197
|
+
Beverages: ['Aecht Schlenkerla Rauchbier', 'Apple Juice']
|
198
|
+
✓ Dish 일치
|
199
|
+
|
200
|
+
테스트 케이스 2: Summer, 10 guests, no children
|
201
|
+
✓ 결과:
|
202
|
+
Dish: Light Salad and a nice Steak
|
203
|
+
Beverages: ['Water']
|
204
|
+
✓ Dish 일치
|
205
|
+
|
206
|
+
테스트 케이스 3: Winter, 2 guests, no children
|
207
|
+
✓ 결과:
|
208
|
+
Dish: Roastbeef
|
209
|
+
Beverages: ['Water']
|
210
|
+
✓ Dish 일치
|
211
|
+
```
|
212
|
+
|
213
|
+
## 사용 예제
|
214
|
+
|
215
|
+
### Claude와 함께 사용
|
216
|
+
|
217
|
+
#### 1. 스키마 확인 후 실행
|
218
|
+
```
|
219
|
+
Claude, simulation 규칙의 스키마를 먼저 확인하고,
|
220
|
+
가을에 4명이 방문하는데 아이가 있을 때 추천 요리와 음료를 알려줘.
|
221
|
+
```
|
222
|
+
|
223
|
+
Claude가 자동으로:
|
224
|
+
1. `get_rule_schema("simulation")` 호출
|
225
|
+
2. Variable 이름 확인: "Season", "How many guests", "Guests with children"
|
226
|
+
3. 올바른 형식으로 입력 구성:
|
227
|
+
```python
|
228
|
+
{
|
229
|
+
"Season": "Fall",
|
230
|
+
"How many guests": 4,
|
231
|
+
"Guests with children": True
|
232
|
+
}
|
233
|
+
```
|
234
|
+
4. `infer_decision()` 호출
|
235
|
+
5. 결과 해석 및 제시
|
236
|
+
|
237
|
+
#### 2. 새 규칙 생성
|
238
|
+
```
|
239
|
+
고객 등급(Gold, Silver, Bronze)과 구매 금액에 따라
|
240
|
+
할인율을 결정하는 DMN 규칙을 만들어줘.
|
241
|
+
"discount_rules"로 저장해.
|
242
|
+
```
|
243
|
+
|
244
|
+
Claude가:
|
245
|
+
1. DMN XML 생성
|
246
|
+
2. `save_rule("discount_rules", xml_content)` 호출
|
247
|
+
3. 저장 확인
|
248
|
+
|
249
|
+
## API 요약
|
250
|
+
|
251
|
+
### Tool: `get_rule_schema` (가장 중요!)
|
252
|
+
|
253
|
+
**왜 중요한가?**
|
254
|
+
- LLM이 올바른 입력 형식을 알 수 있음
|
255
|
+
- Variable 이름의 정확한 철자/대소문자/공백 확인 가능
|
256
|
+
- FEEL 표현식 이름도 제공
|
257
|
+
|
258
|
+
**반환 예시**:
|
259
|
+
```json
|
260
|
+
{
|
261
|
+
"rule_name": "simulation",
|
262
|
+
"engine_type": "pyDMNrules",
|
263
|
+
"variable_names": ["Season", "How many guests", "Guests with children"],
|
264
|
+
"important_note": "⚠️ Use EXACT variable names...",
|
265
|
+
"inputs": {
|
266
|
+
"Season": {
|
267
|
+
"variable_name": "Season",
|
268
|
+
"feel_name": "Data.Season",
|
269
|
+
"description": "Data.Data.Season",
|
270
|
+
"type": "string",
|
271
|
+
"required": true
|
272
|
+
}
|
273
|
+
},
|
274
|
+
"decision_tables": [
|
275
|
+
{"name": "Dish", "hit_policy": "U"},
|
276
|
+
{"name": "Beverages", "hit_policy": "C"}
|
277
|
+
],
|
278
|
+
"example_input": {
|
279
|
+
"Season": null,
|
280
|
+
"How many guests": null,
|
281
|
+
"Guests with children": null
|
282
|
+
}
|
283
|
+
}
|
284
|
+
```
|
285
|
+
|
286
|
+
### Tool: `infer_decision`
|
287
|
+
|
288
|
+
**입력**:
|
289
|
+
```python
|
290
|
+
{
|
291
|
+
"rule_name": "simulation",
|
292
|
+
"context_input": {
|
293
|
+
"Season": "Fall",
|
294
|
+
"How many guests": 4,
|
295
|
+
"Guests with children": True
|
296
|
+
}
|
297
|
+
}
|
298
|
+
```
|
299
|
+
|
300
|
+
**출력**:
|
301
|
+
```python
|
302
|
+
{
|
303
|
+
"result": {
|
304
|
+
"final_result": {
|
305
|
+
"Dish": "Spareribs",
|
306
|
+
"Beverages": ["Aecht Schlenkerla Rauchbier", "Apple Juice"]
|
307
|
+
},
|
308
|
+
"all_results": [...],
|
309
|
+
"decision_count": 2
|
310
|
+
},
|
311
|
+
"trace": [
|
312
|
+
{"step": 1, "action": "input", "data": {...}},
|
313
|
+
{"step": 2, "action": "decision_table", "table": "Dish", "rule_id": "Rule.1"},
|
314
|
+
{"step": 3, "action": "decision_table", "table": "Beverages", ...}
|
315
|
+
],
|
316
|
+
"input_context": {...},
|
317
|
+
"rule_name": "simulation",
|
318
|
+
"execution_time": 0.0035,
|
319
|
+
"rule_schema": {...},
|
320
|
+
"engine_used": "pyDMNrules"
|
321
|
+
}
|
322
|
+
```
|
323
|
+
|
324
|
+
## 설정 방법
|
325
|
+
|
326
|
+
### 1. 의존성 설치
|
327
|
+
```bash
|
328
|
+
cd /Users/uengine/dmn_mcp/pyDMNrules
|
329
|
+
pip install -r requirements_mcp.txt
|
330
|
+
```
|
331
|
+
|
332
|
+
### 2. 테스트
|
333
|
+
```bash
|
334
|
+
python3 test_mcp_with_simulation.py
|
335
|
+
```
|
336
|
+
|
337
|
+
### 3. Claude Desktop 설정
|
338
|
+
|
339
|
+
**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
340
|
+
|
341
|
+
```json
|
342
|
+
{
|
343
|
+
"mcpServers": {
|
344
|
+
"pydmnrules": {
|
345
|
+
"command": "python3",
|
346
|
+
"args": [
|
347
|
+
"/Users/uengine/dmn_mcp/pyDMNrules/pydmnrules_mcp_server.py"
|
348
|
+
]
|
349
|
+
}
|
350
|
+
}
|
351
|
+
}
|
352
|
+
```
|
353
|
+
|
354
|
+
### 4. Claude Desktop 재시작
|
355
|
+
|
356
|
+
## 다음 단계
|
357
|
+
|
358
|
+
### 개선 가능한 점
|
359
|
+
|
360
|
+
1. **타입 정보 강화**
|
361
|
+
- 현재: 모든 타입을 "string"으로 표시
|
362
|
+
- 개선: DMN XML의 typeRef 활용
|
363
|
+
|
364
|
+
2. **입력/출력 구분**
|
365
|
+
- 현재: 모든 Glossary 항목을 입력으로 간주
|
366
|
+
- 개선: Decision Table 분석하여 실제 입력만 표시
|
367
|
+
|
368
|
+
3. **에러 메시지 개선**
|
369
|
+
- pyDMNrules의 에러를 더 친화적으로 변환
|
370
|
+
|
371
|
+
4. **캐싱**
|
372
|
+
- 로드된 규칙을 메모리에 캐싱하여 성능 향상
|
373
|
+
|
374
|
+
5. **Excel 지원**
|
375
|
+
- pyDMNrules는 Excel도 지원하므로 추가 가능
|
376
|
+
|
377
|
+
## 결론
|
378
|
+
|
379
|
+
pyDMNrules MCP Server는 DMN XML 규칙을 LLM이 쉽게 사용할 수 있도록 하는 완전한 MCP 서버입니다.
|
380
|
+
|
381
|
+
**핵심 성공 요소**:
|
382
|
+
- ✅ pyDMNrules의 XML 로드 기능 활용
|
383
|
+
- ✅ Glossary에서 정확한 Variable 이름 추출
|
384
|
+
- ✅ LLM 친화적인 스키마 제공
|
385
|
+
- ✅ 상세한 실행 trace
|
386
|
+
- ✅ 포괄적인 테스트
|
387
|
+
|
388
|
+
**사용 준비 완료**:
|
389
|
+
- 모든 기능 테스트 완료
|
390
|
+
- 문서화 완료
|
391
|
+
- Claude Desktop 설정 가이드 제공
|
392
|
+
|
393
|
+
---
|
394
|
+
|
395
|
+
**작성일**: 2025-10-19
|
396
|
+
**버전**: 1.0.0
|
397
|
+
**엔진**: pyDMNrules
|
398
|
+
**프레임워크**: FastMCP
|
399
|
+
|