A consent evaluation system that uses MeTTa (Meta Type Theory) for declarative rule-based reasoning, combined with deterministic Python checks for reliable consent decisions.
This project evaluates budget proposals for consent based on unresolved objections. It uses MeTTa for pattern matching and rule-based reasoning, while maintaining a Python-side canonical store of facts for deterministic, reliable consent decisions.
Key Features:
- β Dual evaluation system: MeTTa for pattern/routing, Python for canonical decisions
- β Deterministic consent checks: Python-side facts ensure reliable results
- β Interactive web interface: Streamlit app for easy proposal evaluation
- β
Command-line interface: Batch processing via
sim.py - β Comprehensive debugging: View both MeTTa and Python evaluation results
The system uses a hybrid architecture to address the brittleness of MeTTa's negation semantics:
- MeTTa Rules: Define declarative patterns for objections and consent
- Python Facts Store: Maintains canonical state
{ pid: { "raised": set((oid, reviewer)), "resolved": set(oid) } } - Deterministic Checks: Python functions compute consent reliably
- Advisory MeTTa Queries: MeTTa results shown for debugging/comparison
This approach ensures:
- Reliability: Python checks are deterministic and don't depend on MeTTa's negation semantics
- Transparency: Both MeTTa and Python results are visible for comparison
- Flexibility: MeTTa can be extended for complex pattern matching while Python handles simple existence checks
A proposal has consent when:
- The proposal exists (is a
BudgetProposal) - There are no unresolved objections
An objection is unresolved when:
- It has been raised (
ObjectionRaised P O R) - It has not been resolved (
not (ObjectionResolved P O))
- Python 3.12+
- pip
-
Clone the repository:
git clone <repository-url> cd consent-process-MeTTa
-
Create and activate a virtual environment:
python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate
-
Install dependencies:
pip install hyperon streamlit
Launch the interactive web application:
streamlit run streamlit_app.pyThe app will open in your browser. You can:
- Upload JSON files: Use the file uploader to add proposal files
- Load sample proposals: Click the button to load examples from
proposals/folder - View results: See consent status with expandable details for each proposal
- Inspect evaluation: View both Python (canonical) and MeTTa (advisory) results
Process proposals from a folder:
python sim.py proposals/With verbose debugging output:
python sim.py proposals/ -vEach proposal must be a JSON file with the following structure:
{
"id": "ProposalID",
"submitter": "SubmitterName",
"reviewers": ["Reviewer1", "Reviewer2"],
"objections": [
{
"id": "OBJ1",
"reviewer": "ReviewerName",
"relevance": "financial",
"reason": "Missing ROI calculation",
"impact": "medium",
"suggestion": "Add ROI analysis",
"resolved": false
}
]
}id(string): Unique proposal identifiersubmitter(string): Name of the person submitting the proposalreviewers(array of strings): List of reviewersobjections(array): List of objection objects (can be empty)
id(string): Unique objection identifierreviewer(string): Name of reviewer who raised the objectionresolved(boolean): Whether the objection has been resolvedreason(string, optional): Reason for the objectionrelevance(string, optional): Relevance categoryimpact(string, optional): Impact levelsuggestion(string, optional): Suggested resolution
See the proposals/ folder for example files:
proposal.json- Basic proposal with one unresolved objectionproposal_marketing_2025.json- Proposal with no objections (has consent)proposal_research_upgrade.json- Proposal with unresolved objectionproposal_facilities_expansion.json- Proposal with mixed resolved/unresolved objections
The system uses canonical MeTTa rules defined in both streamlit_app.py and sim.py:
;; Type Declarations
(: BudgetProposal P)
(: Proposal P)
(: Objection O)
(: Reviewer R)
;; Core Relations
(SubmittedBy P R)
(ReviewedBy P R)
;; Canonical objection facts
(ObjectionRaised P O R)
(ObjectionResolved P O)
;; Derived predicates
(HasUnresolvedObjection P) :-
(ObjectionRaised P O R)
(not (ObjectionResolved P O))
(HasConsent P) :-
(BudgetProposal P)
(not (HasUnresolvedObjection P))
(ObjectionRaised P O R): ProposalPhas objectionOraised by reviewerR(ObjectionResolved P O): ProposalPhas objectionOresolved(HasUnresolvedObjection P): ProposalPhas at least one unresolved objection(HasConsent P): ProposalPhas consent (no unresolved objections)
- Load Rules: MeTTa rules are loaded into the interpreter
- Parse Proposal: JSON is parsed and validated
- Assert Facts:
- MeTTa facts are asserted:
(BudgetProposal P),(ObjectionRaised P O R), etc. - Python facts are stored:
proposal_facts[pid]["raised"]andproposal_facts[pid]["resolved"]
- MeTTa facts are asserted:
- Evaluate Consent:
- Python check (canonical):
python_has_consent(pid)computes deterministically - MeTTa query (advisory):
(HasConsent P)for comparison/debugging
- Python check (canonical):
- Display Results: Both results shown, Python decision is final
def python_has_unresolved(pid):
"""Check if proposal has unresolved objections."""
facts = proposal_facts.get(pid, {"raised": set(), "resolved": set()})
raised_oids = {oid for (oid, _) in facts["raised"]}
unresolved = raised_oids - facts["resolved"]
return len(unresolved) > 0
def python_has_consent(pid):
"""Check if proposal has consent (no unresolved objections)."""
return not python_has_unresolved(pid)MeTTa's (not ...) negation can be fragile when:
- Predicate arity mismatches cause rules to never match
- Facts aren't asserted correctly
- Interpreter state leaks between runs
- Python facts store: Maintains exact state of raised/resolved objections
- Deterministic checks: Simple set operations (no negation semantics)
- MeTTa for patterns: Use MeTTa for complex pattern matching, not simple existence checks
- Advisory queries: MeTTa results shown for debugging but not used for final decision
β
Consent Evaluation Results
β
Marketing2025 β Approved
π Proposal JSON for Marketing2025 [expandable]
π MeTTa Evaluation Details for Marketing2025 [expandable]
- Python has_consent: β
True
- MeTTa HasConsent: β
True (advisory)
β ResearchUpgrade β Not Approved
π Proposal JSON for ResearchUpgrade [expandable]
π MeTTa Evaluation Details for ResearchUpgrade [expandable]
- Python has_consent: β False
- Unresolved objections: 1
$ python sim.py proposals/ -v
===== CONSENT EVALUATION RESULTS =====
Marketing2025 => True
ResearchUpgrade => False
FacilitiesExpansion => FalseIf you see incorrect MeTTa results:
- Check predicate signatures match exactly:
(ObjectionRaised P O R)not(ObjectionRaised C P O R) - Verify facts are asserted correctly
- Trust the Python result - it's the canonical decision
- Use verbose mode (
-v) to see debugging output
The system resets the MeTTa interpreter:
- Streamlit: On each file upload
- CLI: At the start of each simulation run
This prevents state leakage between runs.
consent-process-MeTTa/
βββ streamlit_app.py # Streamlit web interface
βββ sim.py # CLI batch processor
βββ proposals/ # Example proposal JSON files
β βββ proposal.json
β βββ proposal_marketing_2025.json
β βββ proposal_research_upgrade.json
β βββ proposal_facilities_expansion.json
βββ README.md # This file
streamlit_app.py:
load_rules(metta_instance): Load canonical MeTTa rulesassert_proposal_and_record(): Parse JSON, assert facts, store in Pythonpython_has_unresolved(pid): Check for unresolved objectionspython_has_consent(pid): Check for consent (canonical decision)
sim.py:
load_rules(): Load MeTTa rulesassert_proposal_and_record(path): Process proposal filecheck_consent(pid): Evaluate consent (uses Python as canonical)simulate(folder): Batch process proposals
[Add your license here]
[Add contribution guidelines here]
- Built with MeTTa/Hyperon
- Web interface powered by Streamlit