kele 0.0.1a2__tar.gz → 0.0.1b1__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.
Files changed (106) hide show
  1. kele-0.0.1b1/.driftcheck.toml +27 -0
  2. kele-0.0.1b1/.github/workflows/driftcheck.yml +35 -0
  3. {kele-0.0.1a2 → kele-0.0.1b1}/.github/workflows/lint.yml +18 -2
  4. kele-0.0.1b1/.github/workflows/pr_agent.yml +34 -0
  5. {kele-0.0.1a2 → kele-0.0.1b1}/.github/workflows/release.yml +1 -1
  6. {kele-0.0.1a2 → kele-0.0.1b1}/.github/workflows/test.yml +15 -6
  7. kele-0.0.1b1/.pr_agent.toml +13 -0
  8. {kele-0.0.1a2 → kele-0.0.1b1}/PKG-INFO +11 -2
  9. {kele-0.0.1a2 → kele-0.0.1b1}/README.md +8 -1
  10. {kele-0.0.1a2 → kele-0.0.1b1}/README.zh.md +8 -1
  11. kele-0.0.1b1/assets/cola-icon.svg +129 -0
  12. kele-0.0.1b1/kele/_version.py +1 -0
  13. {kele-0.0.1a2 → kele-0.0.1b1}/kele/config.py +6 -1
  14. {kele-0.0.1a2 → kele-0.0.1b1}/kele/control/builtin_hooks.py +22 -16
  15. {kele-0.0.1a2 → kele-0.0.1b1}/kele/control/callback.py +33 -2
  16. kele-0.0.1b1/kele/control/grounding_selector/_rule_strategies/_scc_sort_strategy.py +110 -0
  17. {kele-0.0.1a2 → kele-0.0.1b1}/kele/executer/executing.py +1 -1
  18. kele-0.0.1b1/kele/grounder/grounded_rule_ds/_nodes/_assertion.py +538 -0
  19. {kele-0.0.1a2 → kele-0.0.1b1}/kele/grounder/grounded_rule_ds/_nodes/_term.py +1 -1
  20. {kele-0.0.1a2 → kele-0.0.1b1}/kele/grounder/grounded_rule_ds/_nodes/_tupletable.py +23 -12
  21. kele-0.0.1b1/kele/grounder/grounded_rule_ds/grounded_class.py +810 -0
  22. {kele-0.0.1a2 → kele-0.0.1b1}/kele/grounder/grounded_rule_ds/rule_check.py +8 -8
  23. {kele-0.0.1a2 → kele-0.0.1b1}/kele/knowledge_bases/builtin_base/builtin_operators.py +23 -1
  24. {kele-0.0.1a2 → kele-0.0.1b1}/kele/main.py +3 -3
  25. {kele-0.0.1a2 → kele-0.0.1b1}/kele/syntax/base_classes.py +65 -31
  26. kele-0.0.1b1/kele/utils.py +60 -0
  27. {kele-0.0.1a2 → kele-0.0.1b1}/pyproject.toml +3 -1
  28. {kele-0.0.1a2 → kele-0.0.1b1}/uv.lock +16 -12
  29. kele-0.0.1a2/kele/_version.py +0 -1
  30. kele-0.0.1a2/kele/grounder/grounded_rule_ds/_nodes/_assertion.py +0 -366
  31. kele-0.0.1a2/kele/grounder/grounded_rule_ds/grounded_class.py +0 -504
  32. {kele-0.0.1a2 → kele-0.0.1b1}/.gitattributes +0 -0
  33. {kele-0.0.1a2 → kele-0.0.1b1}/.github/dependabot.yml +0 -0
  34. {kele-0.0.1a2 → kele-0.0.1b1}/.github/workflows/license_check.yml +0 -0
  35. {kele-0.0.1a2 → kele-0.0.1b1}/.gitignore +0 -0
  36. {kele-0.0.1a2 → kele-0.0.1b1}/.pre-commit-config.yaml +0 -0
  37. {kele-0.0.1a2 → kele-0.0.1b1}/.ruff.toml +0 -0
  38. {kele-0.0.1a2 → kele-0.0.1b1}/CONTRIBUTING.md +0 -0
  39. {kele-0.0.1a2 → kele-0.0.1b1}/Cargo.lock +0 -0
  40. {kele-0.0.1a2 → kele-0.0.1b1}/Cargo.toml +0 -0
  41. {kele-0.0.1a2 → kele-0.0.1b1}/LICENSE +0 -0
  42. {kele-0.0.1a2 → kele-0.0.1b1}/_scripts/ci/examples_static_comment.py +0 -0
  43. {kele-0.0.1a2 → kele-0.0.1b1}/_scripts/ci/run_examples_static_ci.sh +0 -0
  44. {kele-0.0.1a2 → kele-0.0.1b1}/hatch_build.py +0 -0
  45. {kele-0.0.1a2 → kele-0.0.1b1}/kele/__init__.py +0 -0
  46. {kele-0.0.1a2 → kele-0.0.1b1}/kele/_utils.py +0 -0
  47. {kele-0.0.1a2 → kele-0.0.1b1}/kele/control/README_metrics.md +0 -0
  48. {kele-0.0.1a2 → kele-0.0.1b1}/kele/control/__init__.py +0 -0
  49. {kele-0.0.1a2 → kele-0.0.1b1}/kele/control/grounding_selector/__init__.py +0 -0
  50. {kele-0.0.1a2 → kele-0.0.1b1}/kele/control/grounding_selector/_rule_strategies/README.md +0 -0
  51. {kele-0.0.1a2 → kele-0.0.1b1}/kele/control/grounding_selector/_rule_strategies/__init__.py +0 -0
  52. {kele-0.0.1a2 → kele-0.0.1b1}/kele/control/grounding_selector/_rule_strategies/_sequential_strategy.py +0 -0
  53. {kele-0.0.1a2 → kele-0.0.1b1}/kele/control/grounding_selector/_rule_strategies/strategy_protocol.py +0 -0
  54. {kele-0.0.1a2 → kele-0.0.1b1}/kele/control/grounding_selector/_selector_utils.py +0 -0
  55. {kele-0.0.1a2 → kele-0.0.1b1}/kele/control/grounding_selector/_term_strategies/README.md +0 -0
  56. {kele-0.0.1a2 → kele-0.0.1b1}/kele/control/grounding_selector/_term_strategies/__init__.py +0 -0
  57. {kele-0.0.1a2 → kele-0.0.1b1}/kele/control/grounding_selector/_term_strategies/_exhausted_strategy.py +0 -0
  58. {kele-0.0.1a2 → kele-0.0.1b1}/kele/control/grounding_selector/_term_strategies/strategy_protocol.py +0 -0
  59. {kele-0.0.1a2 → kele-0.0.1b1}/kele/control/grounding_selector/rule_selector.py +0 -0
  60. {kele-0.0.1a2 → kele-0.0.1b1}/kele/control/grounding_selector/term_selector.py +0 -0
  61. {kele-0.0.1a2 → kele-0.0.1b1}/kele/control/infer_path.py +0 -0
  62. {kele-0.0.1a2 → kele-0.0.1b1}/kele/control/metrics.py +0 -0
  63. {kele-0.0.1a2 → kele-0.0.1b1}/kele/control/registry.py +0 -0
  64. {kele-0.0.1a2 → kele-0.0.1b1}/kele/control/status.py +0 -0
  65. {kele-0.0.1a2 → kele-0.0.1b1}/kele/egg_equiv.pyi +0 -0
  66. {kele-0.0.1a2 → kele-0.0.1b1}/kele/equality/README.md +0 -0
  67. {kele-0.0.1a2 → kele-0.0.1b1}/kele/equality/__init__.py +0 -0
  68. {kele-0.0.1a2 → kele-0.0.1b1}/kele/equality/_egg_equiv/src/lib.rs +0 -0
  69. {kele-0.0.1a2 → kele-0.0.1b1}/kele/equality/_equiv_elem.py +0 -0
  70. {kele-0.0.1a2 → kele-0.0.1b1}/kele/equality/_utils.py +0 -0
  71. {kele-0.0.1a2 → kele-0.0.1b1}/kele/equality/equivalence.py +0 -0
  72. {kele-0.0.1a2 → kele-0.0.1b1}/kele/executer/__init__.py +0 -0
  73. {kele-0.0.1a2 → kele-0.0.1b1}/kele/grounder/README.md +0 -0
  74. {kele-0.0.1a2 → kele-0.0.1b1}/kele/grounder/__init__.py +0 -0
  75. {kele-0.0.1a2 → kele-0.0.1b1}/kele/grounder/grounded_rule_ds/__init__.py +0 -0
  76. {kele-0.0.1a2 → kele-0.0.1b1}/kele/grounder/grounded_rule_ds/_nodes/__init__.py +0 -0
  77. {kele-0.0.1a2 → kele-0.0.1b1}/kele/grounder/grounded_rule_ds/_nodes/_conn.py +0 -0
  78. {kele-0.0.1a2 → kele-0.0.1b1}/kele/grounder/grounded_rule_ds/_nodes/_op.py +0 -0
  79. {kele-0.0.1a2 → kele-0.0.1b1}/kele/grounder/grounded_rule_ds/_nodes/_root.py +0 -0
  80. {kele-0.0.1a2 → kele-0.0.1b1}/kele/grounder/grounded_rule_ds/_nodes/_rule.py +0 -0
  81. {kele-0.0.1a2 → kele-0.0.1b1}/kele/grounder/grounded_rule_ds/_nodes/_tftable.py +0 -0
  82. {kele-0.0.1a2 → kele-0.0.1b1}/kele/grounder/grounded_rule_ds/_nodes/_typing_polars.py +0 -0
  83. {kele-0.0.1a2 → kele-0.0.1b1}/kele/grounder/grounded_rule_ds/grounded_ds_utils.py +0 -0
  84. {kele-0.0.1a2 → kele-0.0.1b1}/kele/grounder/grounding.py +0 -0
  85. {kele-0.0.1a2 → kele-0.0.1b1}/kele/knowledge_bases/README.md +0 -0
  86. {kele-0.0.1a2 → kele-0.0.1b1}/kele/knowledge_bases/__init__.py +0 -0
  87. {kele-0.0.1a2 → kele-0.0.1b1}/kele/knowledge_bases/builtin_base/__init__.py +0 -0
  88. {kele-0.0.1a2 → kele-0.0.1b1}/kele/knowledge_bases/builtin_base/builtin_concepts.py +0 -0
  89. {kele-0.0.1a2 → kele-0.0.1b1}/kele/knowledge_bases/builtin_base/builtin_facts.py +0 -0
  90. {kele-0.0.1a2 → kele-0.0.1b1}/kele/knowledge_bases/builtin_base/builtin_rules.py +0 -0
  91. {kele-0.0.1a2 → kele-0.0.1b1}/kele/knowledge_bases/fact_base.py +0 -0
  92. {kele-0.0.1a2 → kele-0.0.1b1}/kele/knowledge_bases/ontology_base.py +0 -0
  93. {kele-0.0.1a2 → kele-0.0.1b1}/kele/knowledge_bases/rule_base.py +0 -0
  94. {kele-0.0.1a2 → kele-0.0.1b1}/kele/py.typed +0 -0
  95. {kele-0.0.1a2 → kele-0.0.1b1}/kele/syntax/CONCEPT_README.md +0 -0
  96. {kele-0.0.1a2 → kele-0.0.1b1}/kele/syntax/__init__.py +0 -0
  97. {kele-0.0.1a2 → kele-0.0.1b1}/kele/syntax/_cnf_converter.py +0 -0
  98. {kele-0.0.1a2 → kele-0.0.1b1}/kele/syntax/_sat_solver.py +0 -0
  99. {kele-0.0.1a2 → kele-0.0.1b1}/kele/syntax/connectives.py +0 -0
  100. {kele-0.0.1a2 → kele-0.0.1b1}/kele/syntax/dnf_converter.py +0 -0
  101. {kele-0.0.1a2 → kele-0.0.1b1}/kele/syntax/external.py +0 -0
  102. {kele-0.0.1a2 → kele-0.0.1b1}/kele/syntax/sub_concept.py +0 -0
  103. {kele-0.0.1a2 → kele-0.0.1b1}/kele/syntax/syntacticsugar.py +0 -0
  104. {kele-0.0.1a2 → kele-0.0.1b1}/licensecheck.json +0 -0
  105. {kele-0.0.1a2 → kele-0.0.1b1}/mypy.ini +0 -0
  106. {kele-0.0.1a2 → kele-0.0.1b1}/pytest.ini +0 -0
@@ -0,0 +1,27 @@
1
+ [general]
2
+ enabled = true
3
+ allow_push_on_error = false
4
+
5
+ [docs]
6
+ paths = [
7
+ "README.md",
8
+ "docs/tutorial/zh/**",
9
+ "docs/tutorial/en/**",
10
+ ]
11
+
12
+ [llm]
13
+ base_url = "https://integrate.api.nvidia.com/v1"
14
+ model = "z-ai/glm4.7"
15
+ timeout = 300
16
+ max_retries = 2
17
+
18
+ [tui]
19
+ theme = "default"
20
+ auto_apply = false
21
+
22
+ [cache]
23
+ enabled = true
24
+ dir = ".git/driftcheck_cache"
25
+ ttl = 3600
26
+
27
+ [prompts]
@@ -0,0 +1,35 @@
1
+ name: Documentation Check
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [ main ]
6
+
7
+ jobs:
8
+ driftcheck:
9
+ if: ${{ vars.SUPPRESS_LLM_ACTIONS != 'true' }}
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v6
13
+ with:
14
+ fetch-depth: 0
15
+
16
+ # Optional: checkout tutorial repo into workspace (e.g. docs/tutorial/)
17
+ - uses: actions/checkout@v6
18
+ with:
19
+ repository: msg-bq/msg-bq.github.io
20
+ path: docs/tutorial
21
+ fetch-depth: 1
22
+
23
+ - name: Install ripgrep
24
+ run: sudo apt-get update && sudo apt-get install -y ripgrep
25
+
26
+ - name: Install driftcheck
27
+ run: |
28
+ curl -L https://github.com/deichrenner/driftcheck/releases/latest/download/driftcheck-linux-x86_64 -o driftcheck
29
+ chmod +x driftcheck
30
+ sudo mv driftcheck /usr/local/bin/
31
+
32
+ - name: Check documentation drift
33
+ env:
34
+ DRIFTCHECK_API_KEY: ${{ secrets.DRIFTCHECK_API_KEY }}
35
+ run: driftcheck check --range origin/${{ github.base_ref }}..HEAD --no-tui
@@ -31,6 +31,22 @@ jobs:
31
31
  python-version: 3.13
32
32
  enable-cache: true
33
33
  - name: Install ruff
34
- run: uv sync --group ruff
34
+ run: uv sync --only-group ruff
35
35
  - name: Run ruff
36
- run: uv run ruff check
36
+ run: uv run --no-sync ruff check
37
+
38
+ pydoclint:
39
+ if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
40
+ runs-on: ubuntu-latest
41
+ steps:
42
+ - name: Checkout
43
+ uses: actions/checkout@v6
44
+ - name: Install uv
45
+ uses: astral-sh/setup-uv@v7
46
+ with:
47
+ python-version: 3.13
48
+ enable-cache: true
49
+ - name: Install pydoclint
50
+ run: uv sync --only-group pydoclint
51
+ - name: Run pydoclint (Sphinx/reST docstrings)
52
+ run: uv run --no-sync flake8 kele
@@ -0,0 +1,34 @@
1
+ name: PR Agent
2
+
3
+ on:
4
+ pull_request:
5
+ types: [opened, reopened, ready_for_review, synchronize]
6
+ issue_comment:
7
+ types: [created]
8
+ workflow_dispatch:
9
+
10
+ jobs:
11
+ pr_agent_job:
12
+ if: ${{ vars.SUPPRESS_LLM_ACTIONS != 'true' && github.event.sender.type != 'Bot' && (github.event_name != 'issue_comment' || github.event.issue.pull_request) }}
13
+ runs-on: ubuntu-latest
14
+ permissions:
15
+ issues: write
16
+ pull-requests: write
17
+ contents: write
18
+
19
+ steps:
20
+ - uses: actions/checkout@v6
21
+ - name: PR Agent action step
22
+ uses: qodo-ai/pr-agent@main
23
+ env:
24
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
25
+ NVIDIA_NIM_API_KEY: ${{ secrets.QODO_API_KEY }}
26
+ NVIDIA_NIM_API_BASE: https://integrate.api.nvidia.com/v1/
27
+ config.model: "nvidia_nim/moonshotai/kimi-k2.5"
28
+ config.fallback_models: '["nvidia_nim/moonshotai/kimi-k2.5"]'
29
+ config.custom_model_max_tokens: "128000"
30
+ config.ai_timeout: "600"
31
+ github_action_config.auto_review: "true"
32
+ github_action_config.auto_describe: "false"
33
+ github_action_config.auto_improve: "true"
34
+ github_action_config.pr_actions: '["opened","reopened","ready_for_review","review_requested","synchronize"]'
@@ -193,7 +193,7 @@ jobs:
193
193
  fi
194
194
 
195
195
  - name: Download wheels
196
- uses: actions/download-artifact@v6
196
+ uses: actions/download-artifact@v7
197
197
  with:
198
198
  pattern: wheels-*
199
199
  merge-multiple: true
@@ -15,7 +15,7 @@ permissions:
15
15
 
16
16
  concurrency:
17
17
  group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.issue.number || github.ref }}
18
- cancel-in-progress: ${{ vars.CANCEL_IN_PROGRESS == 'true' }}
18
+ cancel-in-progress: ${{ vars.CANCEL_IN_PROGRESS == 'true' && !(github.event_name == 'push' && github.ref == 'refs/heads/main') }}
19
19
 
20
20
  jobs:
21
21
  precheck:
@@ -210,14 +210,14 @@ jobs:
210
210
  run: uv run maturin develop --skip-install
211
211
 
212
212
  - name: Run examples static (current)
213
- working-directory: "."
213
+ working-directory: curr
214
214
  timeout-minutes: 3
215
215
  id: curr_static
216
216
  continue-on-error: true
217
217
  shell: bash
218
218
  run: |
219
219
  set +e
220
- bash curr/_scripts/ci/run_examples_static_ci.sh curr curr_static.log
220
+ bash _scripts/ci/run_examples_static_ci.sh . curr_static.log
221
221
  code=$?
222
222
  set -e
223
223
  echo "exit_code=$code" >> "$GITHUB_OUTPUT"
@@ -232,15 +232,14 @@ jobs:
232
232
  run: uv run maturin develop --skip-install
233
233
 
234
234
  - name: Run examples static (base/main)
235
- working-directory: "."
235
+ working-directory: base
236
236
  timeout-minutes: 3
237
237
  id: base_static
238
238
  continue-on-error: true
239
239
  shell: bash
240
- # HACK: 等主线有了之后,run这里目录换base,然后就可以设置工作路径了
241
240
  run: |
242
241
  set +e
243
- bash curr/_scripts/ci/run_examples_static_ci.sh base base_static.log
242
+ bash _scripts/ci/run_examples_static_ci.sh . base_static.log
244
243
  code=$?
245
244
  set -e
246
245
  echo "exit_code=$code" >> "$GITHUB_OUTPUT"
@@ -300,3 +299,13 @@ jobs:
300
299
  comment-id: ${{ steps.fc.outputs.comment-id }}
301
300
  body-path: pr_comment.md
302
301
  edit-mode: replace
302
+
303
+ - name: Fail if current examples static failed
304
+ if: ${{ always() && steps.curr_static.outputs.exit_code != '' && steps.curr_static.outputs.exit_code != '0' }}
305
+ shell: bash
306
+ run: |
307
+ curr_exit="${{ steps.curr_static.outputs.exit_code }}"
308
+ if [ "$curr_exit" != "0" ]; then
309
+ echo "Current examples static failed with exit code $curr_exit"
310
+ exit "$curr_exit"
311
+ fi
@@ -0,0 +1,13 @@
1
+ [config]
2
+ model = "nvidia_nim/moonshotai/kimi-k2.5"
3
+ fallback_models = ["nvidia_nim/moonshotai/kimi-k2.5"]
4
+ custom_model_max_tokens = 128000
5
+ ai_timeout = 600
6
+
7
+ [github_action_config]
8
+ auto_review = true
9
+ auto_describe = false
10
+ auto_improve = true
11
+
12
+ [nvidia_nim]
13
+ api_base = "https://integrate.api.nvidia.com/v1/"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kele
3
- Version: 0.0.1a2
3
+ Version: 0.0.1b1
4
4
  Summary: Knowledge Equations based Logic Engine, a forward chaining inference engine with Assertional Logic
5
5
  Author: Bingqian Li, BoYang Zhang, Weiming Hong, Hao Zhang, Yi Zhou
6
6
  Maintainer-email: Bingqian Li <libq2022@alumni.shanghaitech.edu.cn>, Yi Zhou <yi_zhou@ustc.edu.cn>
@@ -11,8 +11,10 @@ Requires-Dist: bidict<1.0,>=0.23
11
11
  Requires-Dist: dacite<2.0,>=1.9
12
12
  Requires-Dist: graphviz==0.20.3
13
13
  Requires-Dist: lark<2.0,>=1.0
14
+ Requires-Dist: mpmath<2.0,>=1.3
14
15
  Requires-Dist: networkx<4.0,>=3.5
15
16
  Requires-Dist: numpy>=2.3.3
17
+ Requires-Dist: pathspec<2.0,>=1.0.4
16
18
  Requires-Dist: polars>=1.32
17
19
  Requires-Dist: prometheus-client>=0.22
18
20
  Requires-Dist: psutil>=7.0
@@ -29,6 +31,10 @@ Description-Content-Type: text/markdown
29
31
 
30
32
  [English](README.md) | [中文](README.zh.md)
31
33
 
34
+ <p align="center">
35
+ <img src="assets/cola-icon.svg" alt="Cola icon" width="120" />
36
+ </p>
37
+
32
38
  <!-- Badges: If services are not configured, badges may show unknown/404; enable as needed. -->
33
39
  [![License](https://img.shields.io/github/license/USTC-KnowledgeComputingLab/KELE.svg)](LICENSE)
34
40
  [![Build](https://github.com/USTC-KnowledgeComputingLab/KELE/actions/workflows/release.yml/badge.svg?branch=main)](https://github.com/USTC-KnowledgeComputingLab/KELE/actions/workflows/release.yml)
@@ -52,7 +58,6 @@ It supports **term-level facts**, **nested terms**, **equivalence axioms**, and
52
58
  - **Equivalence axioms**: convenient equivalence expressions with internal maintenance
53
59
  - **Nested compound terms**: operators can nest to build complex structures
54
60
  - **Implement functions for operators**: implement functions for operators (e.g., arithmetic, equation solving)
55
- - **Built-in hook enabler**: enable built-in hooks for inspection/debugging via `BuiltinHookEnabler`
56
61
 
57
62
  > Implement functions for operators ≈ Prolog meta-predicates / ASP HEX external predicates (not identical semantics, similar usage).
58
63
 
@@ -160,6 +165,10 @@ KELE exposes a top-level `register` hub so you can customize internal strategies
160
165
  * Each task registry supports `register(name)` as a decorator plus `get(name)` helpers.
161
166
  * Additional registries may be introduced in the future; today only rule/term selectors are supported.
162
167
 
168
+ ### 🪝 Builtin hooks
169
+
170
+ BuiltinHook provides built-in hook points for inspection/debugging; enable them with the `BuiltinHookEnabler` switch. Other hook utilities (such as `HookMixin` and `CallBack`) are still under development.
171
+
163
172
  ### 🗺️ Roadmap
164
173
 
165
174
  WIP
@@ -2,6 +2,10 @@
2
2
 
3
3
  [English](README.md) | [中文](README.zh.md)
4
4
 
5
+ <p align="center">
6
+ <img src="assets/cola-icon.svg" alt="Cola icon" width="120" />
7
+ </p>
8
+
5
9
  <!-- Badges: If services are not configured, badges may show unknown/404; enable as needed. -->
6
10
  [![License](https://img.shields.io/github/license/USTC-KnowledgeComputingLab/KELE.svg)](LICENSE)
7
11
  [![Build](https://github.com/USTC-KnowledgeComputingLab/KELE/actions/workflows/release.yml/badge.svg?branch=main)](https://github.com/USTC-KnowledgeComputingLab/KELE/actions/workflows/release.yml)
@@ -25,7 +29,6 @@ It supports **term-level facts**, **nested terms**, **equivalence axioms**, and
25
29
  - **Equivalence axioms**: convenient equivalence expressions with internal maintenance
26
30
  - **Nested compound terms**: operators can nest to build complex structures
27
31
  - **Implement functions for operators**: implement functions for operators (e.g., arithmetic, equation solving)
28
- - **Built-in hook enabler**: enable built-in hooks for inspection/debugging via `BuiltinHookEnabler`
29
32
 
30
33
  > Implement functions for operators ≈ Prolog meta-predicates / ASP HEX external predicates (not identical semantics, similar usage).
31
34
 
@@ -133,6 +136,10 @@ KELE exposes a top-level `register` hub so you can customize internal strategies
133
136
  * Each task registry supports `register(name)` as a decorator plus `get(name)` helpers.
134
137
  * Additional registries may be introduced in the future; today only rule/term selectors are supported.
135
138
 
139
+ ### 🪝 Builtin hooks
140
+
141
+ BuiltinHook provides built-in hook points for inspection/debugging; enable them with the `BuiltinHookEnabler` switch. Other hook utilities (such as `HookMixin` and `CallBack`) are still under development.
142
+
136
143
  ### 🗺️ Roadmap
137
144
 
138
145
  WIP
@@ -2,6 +2,10 @@
2
2
 
3
3
  [中文](README.zh.md) | [English](README.md)
4
4
 
5
+ <p align="center">
6
+ <img src="assets/cola-icon.svg" alt="Cola icon" width="120" />
7
+ </p>
8
+
5
9
  <!-- Badges: If services are not configured, badges may show unknown/404; enable as needed. -->
6
10
  [![License](https://img.shields.io/github/license/USTC-KnowledgeComputingLab/KELE.svg)](LICENSE)
7
11
  [![Build](https://github.com/USTC-KnowledgeComputingLab/KELE/actions/workflows/release.yml/badge.svg?branch=main)](https://github.com/USTC-KnowledgeComputingLab/KELE/actions/workflows/release.yml)
@@ -25,7 +29,6 @@ KELE 是基于[断言逻辑](https://link.springer.com/chapter/10.1007/978-3-319
25
29
  - **等词公理**:便捷表达等价关系,引擎内部自行维护
26
30
  - **可嵌套复合项**:允许嵌套项,算子可互相嵌套构成更复杂的复合结构
27
31
  - **算子的外部实现**:支持使用函数对算子进行自定义“实现”,如加法、解方程等
28
- - **内置 hook 启用器**:通过 `BuiltinHookEnabler` 启用内置 hooks 用于检查/调试
29
32
 
30
33
  > 外部实现 ≈ Prolog 元谓词 / ASP 中 HEX external predicate(语义不完全相同,但使用体验相近)。
31
34
 
@@ -127,6 +130,10 @@ KELE 暴露了顶层 `register` 入口,用于自定义内部策略(如 term
127
130
  * 每个任务注册表支持 `register(name)` 装饰器与 `available()` 查看已注册策略。
128
131
  * 目前仅支持规则/项选择器,后续可能会增加更多注册点。
129
132
 
133
+ ### 🪝 Builtin hooks
134
+
135
+ BuiltinHook 提供内置的 hook 点用于检查/调试,可通过 `BuiltinHookEnabler` 开关启用。其他 hook(如 `HookMixin`、`CallBack`)仍在开发中。
136
+
130
137
  ### 🗺️ Roadmap
131
138
 
132
139
  WIP
@@ -0,0 +1,129 @@
1
+ <svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Polished Soda Cup Icon">
2
+ <defs>
3
+ <!-- Background Card Gradient -->
4
+ <linearGradient id="bgGradNew" x1="32" y1="16" x2="224" y2="240" gradientUnits="userSpaceOnUse">
5
+ <stop offset="0" stop-color="#FFF5E5"/>
6
+ <stop offset="1" stop-color="#FFE0B2"/>
7
+ </linearGradient>
8
+
9
+ <!-- Deep Plastic Lid Gradient -->
10
+ <linearGradient id="lidGradNew" x1="140" y1="56" x2="140" y2="86" gradientUnits="userSpaceOnUse">
11
+ <stop offset="0" stop-color="#FFEDD5"/>
12
+ <stop offset="0.5" stop-color="#FFD1A3"/>
13
+ <stop offset="1" stop-color="#FFB87A"/>
14
+ </linearGradient>
15
+
16
+ <!-- Rich Cup Body Gradient (Generic Red/Orange) -->
17
+ <linearGradient id="cupGradNew" x1="90" y1="100" x2="190" y2="200" gradientUnits="userSpaceOnUse">
18
+ <stop offset="0" stop-color="#FF7F50"/> <!-- Coral Red -->
19
+ <stop offset="0.6" stop-color="#E04020"/> <!-- Deep Red Orange -->
20
+ <stop offset="1" stop-color="#B82E14"/> <!-- Darker shadow red -->
21
+ </linearGradient>
22
+
23
+ <!-- Syrupy Cola Liquid Gradient -->
24
+ <linearGradient id="colaGradNew" x1="140" y1="120" x2="140" y2="200" gradientUnits="userSpaceOnUse">
25
+ <stop offset="0" stop-color="#9C5A1F"/>
26
+ <stop offset="0.7" stop-color="#63310B"/>
27
+ <stop offset="1" stop-color="#421C05"/>
28
+ </linearGradient>
29
+
30
+ <!-- Translucent Straw Gradient -->
31
+ <linearGradient id="strawGradNew" x1="150" y1="40" x2="170" y2="80" gradientUnits="userSpaceOnUse">
32
+ <stop offset="0" stop-color="#FFF0B3"/>
33
+ <stop offset="1" stop-color="#FFC72E"/>
34
+ </linearGradient>
35
+
36
+ <!-- Sharp Glossy Highlight for Plastic -->
37
+ <linearGradient id="glossHighlight" x1="100" y1="90" x2="115" y2="220" gradientUnits="userSpaceOnUse">
38
+ <stop offset="0" stop-color="#FFFFFF" stop-opacity="0.7"/>
39
+ <stop offset="1" stop-color="#FFFFFF" stop-opacity="0.1"/>
40
+ </linearGradient>
41
+
42
+ <!-- Bubble Gradient -->
43
+ <radialGradient id="bubbleGrad" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(0.5 0.5) scale(0.5)">
44
+ <stop stop-color="white" stop-opacity="0.8"/>
45
+ <stop offset="1" stop-color="white" stop-opacity="0.2"/>
46
+ </radialGradient>
47
+
48
+ <!-- Improved Soft Shadow -->
49
+ <filter id="softShadowNew" x="-40%" y="-40%" width="180%" height="180%">
50
+ <feDropShadow dx="0" dy="8" stdDeviation="10" flood-color="#421C05" flood-opacity="0.25"/>
51
+ </filter>
52
+
53
+ <!-- Inner shadow for depth under the lid -->
54
+ <filter id="rimShadow">
55
+ <feOffset dy="4" dx="0"/>
56
+ <feGaussianBlur stdDeviation="3" result="offset-blur"/>
57
+ <feComposite operator="out" in="SourceGraphic" in2="offset-blur" result="inverse"/>
58
+ <feFlood flood-color="black" flood-opacity="0.3" result="color"/>
59
+ <feComposite operator="in" in="color" in2="inverse" result="shadow"/>
60
+ <feComposite operator="over" in="shadow" in2="SourceGraphic"/>
61
+ </filter>
62
+
63
+ <!-- Clip path for liquid and bubbles -->
64
+ <clipPath id="liquidClip">
65
+ <path d="M112 122 Q 140 118, 168 122 L159.8 188.5C159.2 193.2 155.2 196.8 150.5 196.8H129.5C124.8 196.8 120.8 193.2 120.2 188.5L112 122Z"/>
66
+ </clipPath>
67
+ </defs>
68
+
69
+ <!-- Background Card -->
70
+ <rect width="256" height="256" rx="48" fill="url(#bgGradNew)"/>
71
+
72
+ <!-- Main Icon Group with Shadow -->
73
+ <g filter="url(#softShadowNew)">
74
+
75
+ <!-- Straw (Back part) -->
76
+ <path d="M166 42 L150 86" stroke="url(#strawGradNew)" stroke-width="14" stroke-linecap="round"/>
77
+ <path d="M166 42 L150 86" stroke="#C49000" stroke-opacity="0.3" stroke-width="2" fill="none"/>
78
+
79
+ <!-- Cup Body -->
80
+ <g>
81
+ <!-- Main Body Shape -->
82
+ <path d="M88 88H192L176.6 208.8C175.5 217.2 168.4 224 160 224H120C111.6 224 104.5 217.2 103.4 208.8L88 88Z"
83
+ fill="url(#cupGradNew)"/>
84
+
85
+ <!-- Subtle rim shadow at the top of the cup -->
86
+ <path d="M88 88H192V94C192 94 170 98 140 98C110 98 88 94 88 94V88Z" fill="#000000" opacity="0.15"/>
87
+
88
+ <!-- Liquid Inside (Cola) -->
89
+ <g clip-path="url(#liquidClip)">
90
+ <rect x="100" y="115" width="80" height="90" fill="url(#colaGradNew)"/>
91
+
92
+ <!-- Fizz Bubbles Group -->
93
+ <g fill="url(#bubbleGrad)">
94
+ <!-- Small fizz -->
95
+ <circle cx="120" cy="180" r="2"/>
96
+ <circle cx="135" cy="190" r="1.5"/>
97
+ <circle cx="155" cy="175" r="2.5"/>
98
+ <circle cx="140" cy="160" r="1"/>
99
+ <!-- Medium bubbles -->
100
+ <circle cx="128" cy="150" r="4" opacity="0.7"/>
101
+ <circle cx="148" cy="165" r="3.5" opacity="0.8"/>
102
+ <!-- Larger bubbles near surface -->
103
+ <circle cx="135" cy="135" r="5.5" opacity="0.9"/>
104
+ <circle cx="158" cy="142" r="4.5" opacity="0.85"/>
105
+ </g>
106
+ </g>
107
+ <!-- Liquid Top Surface Line (Meniscus) -->
108
+ <path d="M112 122 Q 140 118, 168 122" stroke="#FFFFFF" stroke-opacity="0.4" stroke-width="2" fill="none" stroke-linecap="round"/>
109
+
110
+ <!-- Sharp Glossy Highlight on Cup Body -->
111
+ <path d="M102 92 C 108 130, 106 190, 112 220 L 122 220 C 116 190, 118 130, 112 92 Z" fill="url(#glossHighlight)" opacity="0.8"/>
112
+ </g>
113
+
114
+ <!-- Lid -->
115
+ <g>
116
+ <path d="M96 72C96 63.1634 103.163 56 112 56H168C176.837 56 184 63.1634 184 72V84C184 86.2 182.2 88 180 88H100C97.8 88 96 86.2 96 84V72Z"
117
+ fill="url(#lidGradNew)"/>
118
+ <!-- Sharp Rim Highlight -->
119
+ <path d="M98 86 H182" stroke="#FFFFFF" stroke-opacity="0.8" stroke-width="2" stroke-linecap="round"/>
120
+ <!-- Top Dome Highlight -->
121
+ <ellipse cx="140" cy="62" rx="30" ry="4" fill="white" opacity="0.3"/>
122
+ </g>
123
+
124
+ <!-- Straw (Top emerging part) -->
125
+ <path d="M162 54 L174 20" stroke="url(#strawGradNew)" stroke-width="14" stroke-linecap="round"/>
126
+ <path d="M162 54 L174 20" stroke="white" stroke-opacity="0.4" stroke-width="4" fill="none" stroke-linecap="round" transform="translate(-3, 0)"/>
127
+
128
+ </g>
129
+ </svg>
@@ -0,0 +1 @@
1
+ version = "0.0.1b1"
@@ -50,7 +50,12 @@ class InferenceStrategyConfig:
50
50
  select_rules_num: int | Literal[-1] = -1 # Number of rules to select.
51
51
  select_facts_num: int | Literal[-1] = -1 # Number of facts to select; -1 means all facts.
52
52
  # premise_selection_strategy: Literal[''] = '' # Premise selection algorithm. TODO: Unused.
53
- grounding_rule_strategy: Literal['SequentialCyclic', 'SequentialCyclicWithPriority'] = "SequentialCyclic" # Rule selection strategy in grounding.
53
+ grounding_rule_strategy: Literal[
54
+ 'SequentialCyclic',
55
+ 'SequentialCyclicWithPriority',
56
+ 'SccSort',
57
+ 'ReverseSccSort'
58
+ ] = "SequentialCyclic" # Rule selection strategy in grounding.
54
59
  # executing_sort_strategy: Literal[''] = '' # Execution order strategy. TODO: Unused.
55
60
  grounding_term_strategy: Literal['Exhausted'] = "Exhausted" # Term selection strategy in grounding.
56
61
  question_rule_interval: int = 1 # Insert a question rule every N rules; -1 uses total rule count as the interval.
@@ -1,24 +1,30 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
- from typing import TYPE_CHECKING
4
+ from typing import TYPE_CHECKING, Protocol
5
+
6
+ from kele.utils import format_mapping
5
7
 
6
8
  if TYPE_CHECKING:
7
9
  from collections.abc import Callable, Mapping
8
10
 
9
- from kele.grounder import GroundedRule
10
11
  from kele.grounder.grounded_rule_ds._nodes import _AssertionNode
11
- from kele.syntax import CompoundTerm, Constant, Rule, Variable
12
+ from kele.syntax import CompoundTerm, Constant, Rule, Variable, Assertion
12
13
 
13
14
  logger = logging.getLogger(__name__)
14
15
 
15
16
 
17
+ class AssertionCheckHookMatch(Protocol):
18
+ """the protocol of output func (on_match in register_assertion_check_hook) in assertion check hook"""
19
+ def __call__(self, rule: Rule, assertion: Assertion, combination: dict[Variable, Constant | CompoundTerm], *, result: bool) \
20
+ -> None: ... # noqa: D102
21
+
22
+
16
23
  def register_assertion_check_hook(
17
- grounded_rule: GroundedRule,
18
24
  *,
19
25
  rule_name: str | None = None,
20
26
  vars_filter: Mapping[str, str] | None = None,
21
- on_match: Callable[[Rule, _AssertionNode, dict[Variable, Constant | CompoundTerm], bool], None] | None = None,
27
+ on_match: AssertionCheckHookMatch | None = None,
22
28
  break_on_match: bool = False,
23
29
  ) -> None:
24
30
  """
@@ -33,7 +39,6 @@ def register_assertion_check_hook(
33
39
  print(rule.name, assertion.content, combination, result)
34
40
 
35
41
  register_assertion_check_hook(
36
- grounded_rule,
37
42
  rule_name="rule_3",
38
43
  vars_filter={"p1": "f", "p2": "a", "p13": "b", "p14": "c"},
39
44
  on_match=log_match,
@@ -55,7 +60,7 @@ def register_assertion_check_hook(
55
60
  combination_str = {str(k): str(v) for k, v in combination.items()}
56
61
  if normalized_vars_filter:
57
62
  for key, value in normalized_vars_filter.items():
58
- if combination_str.get(key) != value:
63
+ if combination_str.get(key) != str(value):
59
64
  return
60
65
 
61
66
  if on_match is None:
@@ -63,16 +68,18 @@ def register_assertion_check_hook(
63
68
  "Assertion check hook: rule=%s content=%s combination=%s result=%s",
64
69
  rule,
65
70
  assertion.content,
66
- combination_str,
71
+ format_mapping(combination_str),
67
72
  result,
68
73
  )
69
74
  else:
70
- on_match(rule, assertion, combination, result)
75
+ on_match(rule, assertion.content, combination, result=result)
71
76
 
72
77
  if break_on_match:
73
78
  breakpoint() # noqa: T100
74
79
 
75
- grounded_rule.register_hook("assertion_check", _hook)
80
+ from kele.grounder import GroundedRule # noqa: PLC0415
81
+
82
+ GroundedRule.register_class_hook("assertion_check", _hook)
76
83
 
77
84
 
78
85
  class BuiltinHookEnabler:
@@ -80,12 +87,11 @@ class BuiltinHookEnabler:
80
87
  Enabler for built-in hooks by name.
81
88
 
82
89
  This class does not register hooks by itself. Create an instance and call
83
- ``enable`` to attach a built-in hook to a grounded rule.
90
+ ``enable`` to attach a built-in hook to every grounded rule instance.
84
91
 
85
92
  Example:
86
93
  hooks = BuiltinHookEnabler()
87
94
  hooks.enable(
88
- grounded_rule,
89
95
  "assertion_check",
90
96
  rule_name="rule_3",
91
97
  vars_filter={"p1": "f"},
@@ -103,17 +109,17 @@ class BuiltinHookEnabler:
103
109
  """
104
110
  return sorted(self._hooks)
105
111
 
106
- def enable(self, grounded_rule: GroundedRule, name: str, **kwargs: object) -> None:
112
+ def enable(self, name: str, **kwargs: object) -> None:
107
113
  """
108
114
  Enable a built-in hook by name.
109
115
  """
110
116
  if name not in self._hooks:
111
117
  raise KeyError(f"Unknown built-in hook: {name}")
112
- self._hooks[name](grounded_rule, **kwargs)
118
+ self._hooks[name](**kwargs)
113
119
 
114
- def enable_many(self, grounded_rule: GroundedRule, names: list[str], **kwargs: object) -> None:
120
+ def enable_many(self, names: list[str], **kwargs: object) -> None:
115
121
  """
116
122
  Enable multiple built-in hooks by name.
117
123
  """
118
124
  for hook_name in names:
119
- self.enable(grounded_rule, hook_name, **kwargs)
125
+ self.enable(hook_name, **kwargs)
@@ -1,7 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from collections import defaultdict
4
- from typing import TYPE_CHECKING, Any
4
+ from threading import RLock
5
+ from typing import TYPE_CHECKING, Any, ClassVar
5
6
 
6
7
  if TYPE_CHECKING:
7
8
  from collections.abc import Callable
@@ -18,9 +19,36 @@ class HookMixin:
18
19
 
19
20
  :ivar _hooks: 事件名称到钩子函数列表的映射。
20
21
  """
22
+ _class_hooks: ClassVar[dict[str, list[Callable[..., None]]]]
23
+ _hooks_lock: ClassVar[RLock] = RLock()
24
+
25
+ def __init_subclass__(cls, **kwargs: object) -> None:
26
+ super().__init_subclass__(**kwargs)
27
+ parent_hooks = getattr(cls, "_class_hooks", None)
28
+ class_hooks: dict[str, list[Callable[..., None]]] = defaultdict(list)
29
+ if parent_hooks:
30
+ for event_name, hooks in parent_hooks.items():
31
+ class_hooks[event_name] = list(hooks)
32
+ cls._class_hooks = class_hooks
33
+
21
34
  def __init__(self) -> None:
22
35
  self._hooks: dict[str, list[Callable[..., None]]] = defaultdict(list)
23
36
 
37
+ @classmethod
38
+ def register_class_hook(cls, event_name: str, hook_fn: Callable[..., None], *, unique: bool = True) -> None:
39
+ """
40
+ 在类级别注册 hook,所有实例触发事件时都会执行该 hook。
41
+
42
+ :param event_name: 要监听的事件名称。
43
+ :param hook_fn: 接受任意参数的可调用钩子函数。
44
+ :param unique: 是否防止同一 hook 重复注册。
45
+ """
46
+ with cls._hooks_lock:
47
+ hooks = cls._class_hooks[event_name]
48
+ if unique and any(fn is hook_fn for fn in hooks):
49
+ return
50
+ hooks.append(hook_fn)
51
+
24
52
  def register_hook(self, event_name: str, hook_fn: Callable[..., None]) -> None:
25
53
  """
26
54
  为指定事件注册钩子函数。
@@ -38,7 +66,10 @@ class HookMixin:
38
66
  :param args: 传递给钩子的所有位置参数。
39
67
  :param kwargs: 传递给钩子的所有关键字参数。
40
68
  """
41
- for hook in self._hooks.get(event_name, []):
69
+ with self._hooks_lock:
70
+ class_hooks = list(self.__class__._class_hooks.get(event_name, [])) # noqa: SLF001
71
+ instance_hooks = list(self._hooks.get(event_name, []))
72
+ for hook in class_hooks + instance_hooks:
42
73
  hook(*args, **kwargs)
43
74
 
44
75
  def run_hooks(self, event_name: str, *args: Any, **kwargs: Any) -> None: # noqa: ANN401