| """ |
| Cata Risk Lab: Policy Auditor |
| A Streamlit tool for auditing AI Use Policies |
| """ |
|
|
| import streamlit as st |
| import re |
| from dataclasses import dataclass |
|
|
|
|
| @dataclass |
| class KeywordCheck: |
| keyword: str |
| found: bool |
| weight: int |
| category: str |
|
|
|
|
| def analyze_policy(text: str) -> dict: |
| """Analyze the AI Use Policy text and return findings.""" |
| |
| text_lower = text.lower() |
| |
| |
| keywords_config = [ |
| ("liability", 15, "Legal Protection"), |
| ("human review", 15, "Oversight"), |
| ("data training", 10, "Data Governance"), |
| ("human-in-the-loop", 20, "Critical Safety"), |
| ("accountability", 10, "Legal Protection"), |
| ("transparency", 10, "Ethics"), |
| ("bias", 8, "Fairness"), |
| ("audit", 8, "Compliance"), |
| ("consent", 7, "Privacy"), |
| ("privacy", 7, "Privacy"), |
| ("security", 5, "Security"), |
| ("compliance", 5, "Compliance"), |
| ] |
| |
| results = [] |
| total_possible = sum(k[1] for k in keywords_config) |
| earned_points = 0 |
| |
| for keyword, weight, category in keywords_config: |
| |
| pattern = re.compile(re.escape(keyword), re.IGNORECASE) |
| found = bool(pattern.search(text)) |
| |
| if found: |
| earned_points += weight |
| |
| results.append(KeywordCheck( |
| keyword=keyword, |
| found=found, |
| weight=weight, |
| category=category |
| )) |
| |
| |
| base_score = (earned_points / total_possible) * 100 |
| |
| |
| human_in_loop_check = next((r for r in results if r.keyword == "human-in-the-loop"), None) |
| penalty = 0 |
| if human_in_loop_check and not human_in_loop_check.found: |
| penalty = 15 |
| |
| final_score = max(0, base_score - penalty) |
| |
| return { |
| "results": results, |
| "base_score": base_score, |
| "penalty": penalty, |
| "final_score": round(final_score, 1), |
| "earned_points": earned_points, |
| "total_possible": total_possible |
| } |
|
|
|
|
| def get_score_color(score: float) -> str: |
| """Return color based on score.""" |
| if score >= 80: |
| return "#28a745" |
| elif score >= 60: |
| return "#ffc107" |
| elif score >= 40: |
| return "#fd7e14" |
| else: |
| return "#dc3545" |
|
|
|
|
| def get_score_label(score: float) -> str: |
| """Return label based on score.""" |
| if score >= 80: |
| return "Excellent" |
| elif score >= 60: |
| return "Good" |
| elif score >= 40: |
| return "Needs Improvement" |
| else: |
| return "High Risk" |
|
|
|
|
| def render_badge(): |
| """Render the certification badge.""" |
| badge_html = """ |
| <div style=" |
| display: flex; |
| justify-content: center; |
| margin: 20px 0; |
| "> |
| <div style=" |
| background: linear-gradient(135deg, #1a5f2a 0%, #28a745 50%, #1a5f2a 100%); |
| border: 3px solid #ffd700; |
| border-radius: 15px; |
| padding: 20px 40px; |
| text-align: center; |
| box-shadow: 0 4px 15px rgba(0,0,0,0.3); |
| "> |
| <div style="font-size: 40px; margin-bottom: 5px;">π‘οΈ</div> |
| <div style=" |
| color: #ffd700; |
| font-size: 14px; |
| font-weight: bold; |
| letter-spacing: 2px; |
| margin-bottom: 5px; |
| ">β CERTIFIED</div> |
| <div style=" |
| color: white; |
| font-size: 18px; |
| font-weight: bold; |
| ">Cata Risk Lab</div> |
| <div style=" |
| color: #90EE90; |
| font-size: 12px; |
| margin-top: 5px; |
| ">AI Policy Approved</div> |
| </div> |
| </div> |
| """ |
| st.markdown(badge_html, unsafe_allow_html=True) |
|
|
|
|
| def main(): |
| st.set_page_config( |
| page_title="Cata Risk Lab: Policy Auditor", |
| page_icon="π", |
| layout="wide" |
| ) |
| |
| |
| st.markdown(""" |
| <style> |
| .main-header { |
| text-align: center; |
| padding: 20px; |
| background: linear-gradient(90deg, #1e3a5f, #2d5a87); |
| border-radius: 10px; |
| margin-bottom: 30px; |
| } |
| .main-header h1 { |
| color: white; |
| margin: 0; |
| } |
| .main-header p { |
| color: #a0c4e8; |
| margin: 5px 0 0 0; |
| } |
| .keyword-found { |
| background-color: #d4edda; |
| border-left: 4px solid #28a745; |
| padding: 10px; |
| margin: 5px 0; |
| border-radius: 0 5px 5px 0; |
| } |
| .keyword-missing { |
| background-color: #f8d7da; |
| border-left: 4px solid #dc3545; |
| padding: 10px; |
| margin: 5px 0; |
| border-radius: 0 5px 5px 0; |
| } |
| .score-card { |
| text-align: center; |
| padding: 30px; |
| border-radius: 15px; |
| margin: 20px 0; |
| } |
| </style> |
| """, unsafe_allow_html=True) |
| |
| |
| st.markdown(""" |
| <div class="main-header"> |
| <h1>π Cata Risk Lab: Policy Auditor</h1> |
| <p>Analyze your AI Use Policy for safety and compliance</p> |
| </div> |
| """, unsafe_allow_html=True) |
| |
| |
| col1, col2 = st.columns([1, 1]) |
| |
| with col1: |
| st.subheader("π Paste Your AI Use Policy") |
| |
| policy_text = st.text_area( |
| "Enter your company's AI Use Policy below:", |
| height=400, |
| placeholder="""Paste your AI Use Policy here... |
| |
| Example content might include: |
| - Data handling procedures |
| - Human oversight requirements |
| - Liability clauses |
| - Training data policies |
| - Compliance frameworks""" |
| ) |
| |
| analyze_button = st.button("π Analyze Policy", type="primary", use_container_width=True) |
| |
| with col2: |
| st.subheader("π Analysis Results") |
| |
| if analyze_button and policy_text.strip(): |
| analysis = analyze_policy(policy_text) |
| |
| |
| score = analysis["final_score"] |
| score_color = get_score_color(score) |
| score_label = get_score_label(score) |
| |
| st.markdown(f""" |
| <div class="score-card" style="background: linear-gradient(135deg, {score_color}22, {score_color}44); border: 2px solid {score_color};"> |
| <div style="font-size: 60px; font-weight: bold; color: {score_color};">{score}</div> |
| <div style="font-size: 20px; color: {score_color};">Safety Score / 100</div> |
| <div style="font-size: 16px; color: #666; margin-top: 10px;">{score_label}</div> |
| </div> |
| """, unsafe_allow_html=True) |
| |
| |
| if score > 80: |
| render_badge() |
| else: |
| st.warning("β οΈ Score must be above 80 to receive certification.") |
| |
| |
| if analysis["penalty"] > 0: |
| st.error(f"π¨ **Penalty Applied:** -{analysis['penalty']} points for missing 'Human-in-the-Loop' provision") |
| |
| |
| st.markdown("---") |
| st.subheader("π Detailed Findings") |
| |
| |
| categories = {} |
| for result in analysis["results"]: |
| if result.category not in categories: |
| categories[result.category] = [] |
| categories[result.category].append(result) |
| |
| for category, items in categories.items(): |
| with st.expander(f"π {category}", expanded=True): |
| for item in items: |
| if item.found: |
| st.markdown(f""" |
| <div class="keyword-found"> |
| β
<strong>{item.keyword.title()}</strong> |
| <span style="float: right; color: #28a745;">+{item.weight} pts</span> |
| </div> |
| """, unsafe_allow_html=True) |
| else: |
| st.markdown(f""" |
| <div class="keyword-missing"> |
| β <strong>{item.keyword.title()}</strong> - Not found |
| <span style="float: right; color: #dc3545;">0/{item.weight} pts</span> |
| </div> |
| """, unsafe_allow_html=True) |
| |
| |
| st.markdown("---") |
| found_count = sum(1 for r in analysis["results"] if r.found) |
| total_count = len(analysis["results"]) |
| |
| col_a, col_b, col_c = st.columns(3) |
| with col_a: |
| st.metric("Keywords Found", f"{found_count}/{total_count}") |
| with col_b: |
| st.metric("Points Earned", f"{analysis['earned_points']}/{analysis['total_possible']}") |
| with col_c: |
| st.metric("Penalties", f"-{analysis['penalty']}" if analysis['penalty'] > 0 else "None") |
| |
| elif analyze_button: |
| st.warning("Please paste your AI Use Policy text to analyze.") |
| else: |
| st.info("π Paste your policy text and click 'Analyze Policy' to begin.") |
| |
| |
| st.markdown("---") |
| st.subheader("π― What We Check For") |
| |
| checks = [ |
| ("Liability", "Legal protection clauses"), |
| ("Human Review", "Manual oversight processes"), |
| ("Data Training", "Training data governance"), |
| ("Human-in-the-Loop", "Critical safety requirement"), |
| ("Accountability", "Responsibility frameworks"), |
| ("Transparency", "Disclosure practices"), |
| ("Bias", "Fairness considerations"), |
| ("Audit", "Review mechanisms"), |
| ("Consent", "User permission protocols"), |
| ("Privacy", "Data protection measures"), |
| ] |
| |
| for keyword, description in checks: |
| st.markdown(f"β’ **{keyword}**: {description}") |
| |
| |
| st.markdown("---") |
| st.markdown(""" |
| <div style="text-align: center; color: #666; padding: 20px;"> |
| <p>π Cata Risk Lab Policy Auditor | Helping organizations build safer AI practices</p> |
| </div> |
| """, unsafe_allow_html=True) |
|
|
|
|
| if __name__ == "__main__": |
| main() |