""" Operon Quorum Sensing -- Multi-Agent Voting Simulator ===================================================== Configure a panel of agents (name, weight, vote, confidence), pick a voting strategy, and run the vote. The app shows the aggregated result and a comparison across all 7 strategies. No API keys required -- votes are supplied directly, and the real aggregation code from QuorumSensing._aggregate_votes() does the math. Run locally: pip install gradio python space-quorum/app.py Deploy to HuggingFace Spaces: Copy this directory to a new HF Space with sdk=gradio. """ import sys from pathlib import Path import gradio as gr # Allow importing operon_ai from the repo root when running locally _repo_root = Path(__file__).resolve().parent.parent if str(_repo_root) not in sys.path: sys.path.insert(0, str(_repo_root)) from operon_ai import ( QuorumSensing, VotingStrategy, VoteType, Vote, QuorumResult, ATP_Store, ) # --------------------------------------------------------------------------- # Preset scenarios # --------------------------------------------------------------------------- VOTE_MAP = {"Permit": VoteType.PERMIT, "Block": VoteType.BLOCK, "Abstain": VoteType.ABSTAIN} # Each preset: list of (name, weight, vote_str, confidence) PRESETS: dict[str, dict] = { "(custom)": {"agents": [], "strategy": "Majority", "threshold": ""}, "Unanimous agreement": { "agents": [ ("Sentinel", 1.0, "Permit", 0.95), ("Guardian", 1.0, "Permit", 0.90), ("Watcher", 1.0, "Permit", 0.85), ("Auditor", 1.0, "Permit", 0.92), ("Verifier", 1.0, "Permit", 0.88), ], "strategy": "Unanimous", "threshold": "", }, "Split vote (3-2)": { "agents": [ ("Alpha", 1.0, "Permit", 0.80), ("Beta", 1.0, "Permit", 0.75), ("Gamma", 1.0, "Permit", 0.70), ("Delta", 1.0, "Block", 0.85), ("Epsilon", 1.0, "Block", 0.90), ], "strategy": "Majority", "threshold": "", }, "Expert override": { "agents": [ ("Expert", 5.0, "Block", 0.95), ("Junior-1", 1.0, "Permit", 0.70), ("Junior-2", 1.0, "Permit", 0.65), ("Junior-3", 1.0, "Permit", 0.60), ("Junior-4", 1.0, "Permit", 0.55), ], "strategy": "Weighted", "threshold": "", }, "Low confidence": { "agents": [ ("Uncertain-1", 1.0, "Permit", 0.20), ("Uncertain-2", 1.0, "Permit", 0.25), ("Uncertain-3", 1.0, "Permit", 0.15), ("Hesitant", 1.0, "Permit", 0.30), ("Guessing", 1.0, "Permit", 0.10), ], "strategy": "Confidence", "threshold": "", }, "Emergency quorum": { "agents": [ ("Responder-1", 1.0, "Permit", 0.90), ("Responder-2", 1.0, "Permit", 0.85), ("Offline-1", 1.0, "Abstain", 0.00), ("Offline-2", 1.0, "Abstain", 0.00), ("Offline-3", 1.0, "Abstain", 0.00), ], "strategy": "Threshold", "threshold": "2", }, "Byzantine voting": { "agents": [ ("Malicious-1", 2.0, "Block", 0.95), ("Malicious-2", 2.0, "Block", 0.90), ("Honest-1", 1.0, "Permit", 0.85), ("Honest-2", 1.0, "Permit", 0.80), ("Honest-3", 1.0, "Permit", 0.75), ], "strategy": "Weighted", "threshold": "", }, "Abstention majority": { "agents": [ ("Abstainer-1", 1.0, "Abstain", 0.50), ("Abstainer-2", 1.0, "Abstain", 0.50), ("Abstainer-3", 1.0, "Abstain", 0.50), ("Voter-1", 1.0, "Permit", 0.80), ("Voter-2", 1.0, "Block", 0.85), ], "strategy": "Threshold", "threshold": "1", }, "Dictatorial weight": { "agents": [ ("Dictator", 100.0, "Block", 0.99), ("Citizen-1", 1.0, "Permit", 0.90), ("Citizen-2", 1.0, "Permit", 0.85), ("Citizen-3", 1.0, "Permit", 0.80), ("Citizen-4", 1.0, "Permit", 0.75), ], "strategy": "Weighted", "threshold": "", }, } STRATEGY_MAP = { "Majority": VotingStrategy.MAJORITY, "Supermajority": VotingStrategy.SUPERMAJORITY, "Unanimous": VotingStrategy.UNANIMOUS, "Weighted": VotingStrategy.WEIGHTED, "Confidence": VotingStrategy.CONFIDENCE, "Bayesian": VotingStrategy.BAYESIAN, "Threshold": VotingStrategy.THRESHOLD, } STRATEGY_DESCRIPTIONS = { "Majority": ">50% permits required", "Supermajority": ">66% permits required", "Unanimous": "All must permit (zero blocks)", "Weighted": "Weight-adjusted majority (weight * confidence)", "Confidence": "Only votes above 0.3 confidence count", "Bayesian": "Bayesian belief update from uniform prior", "Threshold": "Fixed count of permits required", } # --------------------------------------------------------------------------- # Core logic # --------------------------------------------------------------------------- def _build_votes( names: list[str], weights: list[float], vote_strs: list[str], confidences: list[float], ) -> list[Vote]: """Build Vote objects from parallel lists of agent data.""" votes = [] for name, weight, vote_str, conf in zip(names, weights, vote_strs, confidences): if not name.strip(): continue vote_type = VOTE_MAP.get(vote_str, VoteType.ABSTAIN) votes.append(Vote( agent_id=name.strip(), vote_type=vote_type, confidence=conf, weight=weight, )) return votes def _run_with_strategy( quorum: QuorumSensing, votes: list[Vote], strategy: VotingStrategy, threshold: float | None, ) -> QuorumResult: """Run aggregation with a specific strategy.""" quorum.strategy = strategy quorum.custom_threshold = threshold return quorum._aggregate_votes(votes) def run_simulation( name1, weight1, vote1, conf1, name2, weight2, vote2, conf2, name3, weight3, vote3, conf3, name4, weight4, vote4, conf4, name5, weight5, vote5, conf5, strategy_str, threshold_str, ) -> tuple[str, str, str]: """Run the quorum vote simulation. Returns (decision_html, vote_breakdown_md, strategy_comparison_md). """ names = [name1, name2, name3, name4, name5] weights = [weight1, weight2, weight3, weight4, weight5] vote_strs = [vote1, vote2, vote3, vote4, vote5] confidences = [conf1, conf2, conf3, conf4, conf5] votes = _build_votes(names, weights, vote_strs, confidences) if not votes: return "Configure at least one agent.", "", "" strategy = STRATEGY_MAP.get(strategy_str, VotingStrategy.MAJORITY) threshold = float(threshold_str) if threshold_str.strip() else None # Create QuorumSensing with matching agent count budget = ATP_Store(budget=500) n = len(votes) quorum = QuorumSensing(n_agents=n, budget=budget, strategy=strategy, threshold=threshold, silent=True) # Run primary vote result = _run_with_strategy(quorum, votes, strategy, threshold) # --- Decision banner --- if result.reached: decision_color = "#16a34a" if result.decision == VoteType.PERMIT else "#dc2626" decision_bg = "#f0fdf4" if result.decision == VoteType.PERMIT else "#fef2f2" border = "#22c55e" if result.decision == VoteType.PERMIT else "#ef4444" reached_text = "QUORUM REACHED" else: decision_color = "#9ca3af" decision_bg = "#f9fafb" border = "#d1d5db" reached_text = "QUORUM NOT REACHED" decision_label = result.decision.value.upper() decision_html = ( f'