import streamlit as st import pandas as pd import json from datasets import load_dataset import ast from pythen import RuleTreeEvaluator st.set_page_config(page_title="GDPR Cases Demo", layout="wide") st.title("🏛️ GDPR Cases - Step-by-Step Rule Evaluation") st.markdown("Interactive demo of GDPR formalization with Pythen rule tree evaluation") # Load dataset @st.cache_data def load_gdpr_dataset(): try: dataset = load_dataset("nguyenthanhasia/gdpr-cases") df = dataset['train'].to_pandas() return df except Exception as e: st.error(f"Error loading dataset: {e}") return None df = load_gdpr_dataset() if df is not None: # Sidebar for sample selection st.sidebar.header("📋 Sample Selection") sample_idx = st.sidebar.selectbox( "Select a sample:", range(len(df)), format_func=lambda i: f"Sample {i+1} (Article {df.iloc[i]['article']}, ID: {df.iloc[i]['id']})" ) # Get selected sample sample = df.iloc[sample_idx] # Display sample information st.header(f"Sample #{sample_idx + 1}") col1, col2 = st.columns(2) with col1: st.metric("Article", f"GDPR Article {sample['article']}") with col2: st.metric("Quality Score", f"{sample['average_score']:.1f}/100") # Scenario st.subheader("📝 Scenario") st.write(sample['scenario']) # Facts st.subheader("📌 Facts") try: facts = ast.literal_eval(sample['facts']) if isinstance(sample['facts'], str) else sample['facts'] if isinstance(facts, list): facts_list = facts for i, fact in enumerate(facts_list, 1): st.write(f"{i}. `{fact}`") else: facts_list = [facts] st.write(facts) except: facts_list = [sample['facts']] st.write(sample['facts']) # Rule Tree st.subheader("🌳 Rule Tree (Pythen Format)") try: rule_tree = json.loads(sample['rule_tree']) if isinstance(sample['rule_tree'], str) else sample['rule_tree'] st.json(rule_tree) except: st.code(sample['rule_tree'], language="json") rule_tree = None # Evaluation Results st.subheader("✅ Evaluation Results") col1, col2, col3, col4 = st.columns(4) with col1: st.metric("Scenario Score", f"{sample['scenario_score']:.1f}") with col2: st.metric("Representation Score", f"{sample['representation_score']:.1f}") with col3: st.metric("Legal Score", f"{sample['legal_score']:.1f}") with col4: st.metric("Logical Pass", "✓ Yes" if sample['logical_pass'] else "✗ No") # Ground Truth st.subheader("🎯 Ground Truth") label_text = "TRUE" if sample['label'] else "FALSE" st.info(f"Expected Output: **{label_text}**") # Step-by-step evaluation with Pythen st.subheader("🔍 Step-by-Step Rule Evaluation (Pythen)") try: if rule_tree and isinstance(rule_tree, list): # Create evaluator evaluator = RuleTreeEvaluator(rule_tree) # Get the root predicate (usually the first one with "art" in the name or the first one) root_predicate = None for rule in rule_tree: if isinstance(rule, dict) and 'p' in rule: root_predicate = rule['p'] break if root_predicate: # Evaluate computed_result = evaluator.evaluate(facts_list, root_predicate) # Display evaluation details with st.expander("Show evaluation details", expanded=True): st.write("### Pythen Evaluation Process") # Display inputs st.write("**Inputs:**") col1, col2 = st.columns(2) with col1: st.write("**Facts:**") for fact in facts_list: st.write(f"- `{fact}`") with col2: st.write("**Target Predicate:**") st.write(f"`{root_predicate}`") # Display rule structure st.write("**Rule Structure:**") for i, rule in enumerate(rule_tree, 1): if isinstance(rule, dict): p = rule.get('p', 'unknown') op = rule.get('op', 'UNKNOWN') conditions = rule.get('conditions', []) exceptions = rule.get('exceptions', []) st.write(f"**Rule {i}: {p}**") st.write(f"- Operator: `{op}`") st.write(f"- Conditions: {conditions}") if exceptions: st.write(f"- Exceptions: {exceptions}") # Display result st.write("---") st.write("**Evaluation Result:**") result_color = "green" if computed_result == sample['label'] else "orange" result_match = "✓ Matches Ground Truth" if computed_result == sample['label'] else "⚠ Differs from Ground Truth" col1, col2 = st.columns(2) with col1: st.write(f"**Computed Result:** `{computed_result}`") with col2: st.write(f"**Expected Result:** `{sample['label']}`") st.write(f"**Status:** {result_match}") # Explanation st.write("---") st.write("### How Pythen Evaluates:") st.write(""" 1. **Parse Rule Tree**: Pythen reads the hierarchical rule structure 2. **Extract Facts**: Identifies which facts are present in the case 3. **Traverse Tree**: Starting from root predicate, evaluates each node 4. **Apply Operators**: - `ANY`: Returns TRUE if at least one condition is satisfied - `ALL`: Returns TRUE only if all conditions are satisfied 5. **Handle Exceptions**: Subtracts exceptions from the result 6. **Derive Label**: Final boolean result represents the legal outcome """) else: st.warning("Could not find root predicate in rule tree") else: st.warning("Rule tree format not recognized or empty") except Exception as e: st.error(f"Error during evaluation: {str(e)}") st.write("Make sure the rule tree and facts are properly formatted.") # Dataset statistics st.sidebar.header("📊 Dataset Statistics") st.sidebar.metric("Total Samples", len(df)) st.sidebar.metric("Avg Quality Score", f"{df['average_score'].mean():.1f}") articles = df['article'].unique() st.sidebar.write(f"**GDPR Articles**: {', '.join(map(str, sorted(articles)))}") else: st.error("Failed to load dataset. Please check your internet connection.")