import gradio as gr import json import os import time import tempfile import re import requests from pathlib import Path from datetime import datetime from typing import List, Dict from difflib import SequenceMatcher from gtts import gTTS from pypdf import PdfReader import PyPDF2 from docx import Document as DocxDocument from docx.enum.text import WD_ALIGN_PARAGRAPH SAVE_DIR = Path("/data/saved_docs") SAVE_DIR.mkdir(parents=True, exist_ok=True) # Qwen3-Coder GGUF from the user's HF bucket — ZeroGPU, no Inference Providers # BUCKET_WIRE_V2 BUCKET_ID = "Basementup/Qwen3-Coder-30B-A3B-asym-2bitexp-GGUF-bucket" GGUF_NAME = "Qwen3-Coder-30B-A3B-asym-2bitexp.gguf" MODEL_DIR = Path("/data/models") MODEL_DIR.mkdir(parents=True, exist_ok=True) GGUF_PATH = MODEL_DIR / GGUF_NAME # ========================= # SHARED: LOCAL GGUF / TTS # ========================= import spaces from huggingface_hub import HfApi _llm = None def _ensure_gguf() -> Path: """Download the GGUF from the private bucket into persistent /data once.""" if GGUF_PATH.exists() and GGUF_PATH.stat().st_size > 1_000_000_000: return GGUF_PATH token = ( os.environ.get("HF_TOKEN") or os.environ.get("HUGGINGFACE_HUB_TOKEN") or os.environ.get("HUGGING_FACE_HUB_TOKEN") or True ) api = HfApi(token=token) print(f"Downloading {GGUF_NAME} from bucket {BUCKET_ID} ...") api.download_bucket_files( bucket_id=BUCKET_ID, files=[(GGUF_NAME, str(GGUF_PATH))], token=token, ) if not GGUF_PATH.exists(): raise FileNotFoundError(f"GGUF missing after download: {GGUF_PATH}") return GGUF_PATH try: _ensure_gguf() except Exception as _prefetch_err: print(f"GGUF prefetch deferred: {_prefetch_err}") def _preload_cuda_libs(): """Load CUDA 12 runtime libs from the nvidia-* pip wheels so libllama.so can find them.""" import ctypes, glob, site roots = list(site.getsitepackages()) + [site.getusersitepackages()] for pattern in ("nvidia/cuda_runtime/lib/libcudart.so.12*", "nvidia/cublas/lib/libcublasLt.so.12*", "nvidia/cublas/lib/libcublas.so.12*"): for root in roots: hits = sorted(glob.glob(os.path.join(root, pattern))) if hits: try: ctypes.CDLL(hits[0], mode=ctypes.RTLD_GLOBAL) except OSError as e: print(f"CUDA preload failed for {hits[0]}: {e}") break def _get_llm(): global _llm if _llm is not None: return _llm _preload_cuda_libs() from llama_cpp import Llama path = _ensure_gguf() _llm = Llama( model_path=str(path), n_ctx=8192, n_gpu_layers=-1, n_batch=512, verbose=False, ) return _llm @spaces.GPU(duration=180, size="xlarge") def call_llama(prompt, user_token=None, max_tokens=1200): """ Runs the bucket Qwen3-Coder GGUF on free ZeroGPU via llama.cpp. Does not call Hugging Face Inference Providers. """ try: llm = _get_llm() messages = [{"role": "user", "content": str(prompt)[:12000]}] out = llm.create_chat_completion( messages=messages, max_tokens=min(int(max_tokens or 1200), 1024), temperature=0.4, top_p=0.9, ) text = (out.get("choices") or [{}])[0].get("message", {}).get("content") or "" return str(text).strip() or "[Empty model response]" except Exception as e: return f"Model error: {e}" def make_tts(text): try: if not text or not str(text).strip(): return None # Increased limit to handle "entire case readout" clean_text = str(text).strip()[:15000] tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") tmp.close() tts = gTTS(text=clean_text, lang="en", slow=False) tts.save(tmp.name) return tmp.name except Exception as e: print(f"TTS error: {e}") return None # ========================= # METABOLIC PHOENIX # ========================= def _file_to_path(path): """Normalise Gradio file payloads (str / FileData / dict) to a local path.""" if not path: return "" if isinstance(path, dict): return path.get("path") or path.get("name") or "" if isinstance(path, (str, bytes, Path)): return str(path) # Gradio FileData-like objects for attr in ("path", "name"): if hasattr(path, attr): value = getattr(path, attr) if value: return str(value) return str(path) def extract_pdf_text(path): path = _file_to_path(path) if not path: return "" try: reader = PdfReader(path) pages = [] for i, page in enumerate(reader.pages): text = page.extract_text() or "" if text.strip(): pages.append(f"--- Page {i + 1} ---\n{text.strip()}") if not pages: return "[No extractable text — may be a scanned PDF]" full = "\n\n".join(pages) return full[:14000] + "\n\n[...truncated]" if len(full) > 14000 else full except Exception as e: return f"[PDF read error: {e}]" def phoenix_analyse(pdf_file): if not pdf_file: return "Please upload a PDF.", "", "", "" doc = extract_pdf_text(pdf_file) if not doc or doc.startswith("["): return doc or "No text extracted.", "", "", "" base = f"DOCUMENT:\n\n{doc[:10000]}\n\n{'=' * 60}\n\n" r1 = call_llama( base + "You are Introbot — part of the Metabolic Phoenix Framework by Dwayne Anthony Brian Galloway.\n\n" + "From this document extract every key fact, who said it and when; every admission; every denial; every contradiction; every gap; and any statutory duties triggered.\n\n" + "Format as a clean numbered FACT LEDGER." ) r2 = call_llama( base + "You are RHB 2.0 + CPF — part of the Metabolic Phoenix Framework by Dwayne Anthony Brian Galloway.\n\n" + "Map every institution to every statutory duty. Mark COMPLIANT or NON-COMPLIANT with evidence. Identify legal errors, causal chain, Equality Act 2010 issues especially s.13 and s.20, and remedies.\n\n" + "Be forensic. Cite statute sections where possible." ) r3 = call_llama( base + "You are Phoenix Badger — part of the Metabolic Phoenix Framework by Dwayne Anthony Brian Galloway.\n\n" + "Run four Ouroboros rings:\n\n" + "RING A — FORWARD SCAN: anomalies, evasion tactics and red flags.\n\n" + "RING B — BACKWARD SWEEP: decisions that would be decided differently under current law.\n\n" + "RING C — TYPOLOGY MUTATION: patterns of institutional misconduct.\n\n" + "RING D — DRIFT CORRECTION: internal contradictions and inconsistencies.\n\n" + "End with ESCALATION MAP." ) r4 = call_llama( f"You are Layoutbot — part of the Metabolic Phoenix Framework by Dwayne Anthony Brian Galloway.\n\n" f"DOCUMENT EXTRACT:\n{doc[:3000]}\n\n" f"FACT LEDGER:\n{r1[:1500]}\n\n" f"COMPLIANCE ANALYSIS:\n{r2[:1500]}\n\n" f"LOOP OUTPUT:\n{r3[:1500]}\n\n" f"{'=' * 60}\n\n" "Produce a complete litigation-ready case summary:\n\n" "EXECUTIVE SUMMARY — plain English, what happened\n" "KEY BREACHES — each with statute\n" "TOP 5 ARGUMENTS — strongest first\n" "RECOMMENDED NEXT STEPS — in priority order\n" "ESCALATION MAP — which body gets what and when\n\n" "Ms. Nala | Injectiv8 | Dwayne Anthony Brian Galloway" ) return r1, r2, r3, r4 def save_phoenix_analysis(r1, r2, r3, r4): if not r1: return "Nothing to save — run an analysis first." ts = time.strftime("%Y-%m-%d %H:%M") doc_id = str(int(time.time() * 1000)) content = ( "METABOLIC PHOENIX FRAMEWORK — FULL ANALYSIS\n" f"Generated: {ts}\n" "Ms. Nala | Injectiv8 | Dwayne Anthony Brian Galloway\n\n" f"{'=' * 60}\n\nCYCLE 1 — FACT LEDGER\n\n{r1}\n\n" f"{'=' * 60}\n\nCYCLE 2 — COMPLIANCE ANALYSIS\n\n{r2}\n\n" f"{'=' * 60}\n\nCYCLE 3 — OUROBOROS LOOPS\n\n{r3}\n\n" f"{'=' * 60}\n\nCYCLE 4 — FULL CASE OUTPUT\n\n{r4}" ) try: index_path = SAVE_DIR / "index.json" library = json.loads(index_path.read_text(encoding="utf-8")) if index_path.exists() else [] entry = {"id": doc_id, "title": f"Analysis — {ts}", "content": content, "saved_at": ts} library.insert(0, entry) index_path.write_text(json.dumps(library, indent=2), encoding="utf-8") (SAVE_DIR / f"{doc_id}.txt").write_text(content, encoding="utf-8") return f"Saved: Analysis — {ts}" except Exception as e: return f"Save error: {e}" def phoenix_export_docx(r1, r2, r3, r4): try: if not r1: return None tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".docx") tmp.close() doc = DocxDocument() title = doc.add_heading("METABOLIC PHOENIX FRAMEWORK", 0) title.alignment = WD_ALIGN_PARAGRAPH.CENTER doc.add_paragraph(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") doc.add_paragraph("Ms. Nala | Injectiv8 | Dwayne Anthony Brian Galloway") doc.add_paragraph("-" * 30) sections = [ ("CYCLE 1 — FACT LEDGER", r1), ("CYCLE 2 — COMPLIANCE ANALYSIS", r2), ("CYCLE 3 — OUROBOROS LOOPS", r3), ("CYCLE 4 — FULL CASE OUTPUT", r4) ] for header, content in sections: doc.add_heading(header, level=1) doc.add_paragraph(content) doc.add_page_break() doc.save(tmp.name) return tmp.name except Exception as e: print(f"Phoenix DOCX export error: {e}") return None def load_library(): index_path = SAVE_DIR / "index.json" try: return json.loads(index_path.read_text(encoding="utf-8")) if index_path.exists() else [] except Exception: return [] def lib_choices(library): return [f"{e['title']} ({e['saved_at']})" for e in library] def choice_to_entry(library, choice): choices = lib_choices(library) try: return library[choices.index(choice)] except Exception: return None def lib_refresh(): library = load_library() return gr.update(choices=lib_choices(library), value=None), "" def lib_view(selection): library = load_library() if not selection: return "" entry = choice_to_entry(library, selection) return entry["content"] if entry else "Not found." def lib_download(selection): library = load_library() if not selection: return gr.update(visible=False), "Select a document first." entry = choice_to_entry(library, selection) if not entry: return gr.update(visible=False), "Not found." file_path = SAVE_DIR / f"{entry['id']}.txt" if file_path.exists(): return gr.update(value=str(file_path), visible=True), "Ready." return gr.update(visible=False), "File missing." def lib_delete(selection): library = load_library() if not selection: return gr.update(choices=lib_choices(library), value=None), "Select a document." entry = choice_to_entry(library, selection) if not entry: return gr.update(choices=lib_choices(library), value=None), "Not found." library = [x for x in library if x["id"] != entry["id"]] (SAVE_DIR / "index.json").write_text(json.dumps(library, indent=2), encoding="utf-8") file_path = SAVE_DIR / f"{entry['id']}.txt" if file_path.exists(): file_path.unlink() return gr.update(choices=lib_choices(library), value=None), "Deleted." # ========================= # SEAMSTRESSES DOCUMENT PROCESSING # ========================= class DocumentParser: @staticmethod def extract_text_from_pdf(file_path: str) -> str: try: with open(file_path, "rb") as f: reader = PyPDF2.PdfReader(f) text = "" for page in reader.pages: extracted = page.extract_text() if extracted: text += extracted + "\n" return text except Exception as e: return f"Error reading PDF: {str(e)}" @staticmethod def extract_text_from_docx(file_path: str) -> str: try: doc = DocxDocument(file_path) return "\n\n".join([p.text for p in doc.paragraphs if p.text.strip()]) except Exception as e: return f"Error reading DOCX: {str(e)}" @staticmethod def extract_text_from_file(file_path: str) -> str: file_path = _file_to_path(file_path) if not file_path: return "Error: No file path provided" try: lower = file_path.lower() if lower.endswith(".pdf"): return DocumentParser.extract_text_from_pdf(file_path) if lower.endswith(".docx"): return DocumentParser.extract_text_from_docx(file_path) with open(file_path, "r", encoding="utf-8", errors="ignore") as f: return f.read() except Exception as e: return f"Error reading file: {str(e)}" @staticmethod def parse_document(text: str) -> Dict: paragraphs = [p.strip() for p in text.split("\n\n") if p.strip()] sentences = [] for paragraph in paragraphs: sentences.extend([s.strip() + "." for s in paragraph.split(".") if s.strip()]) return { "full_text": text, "paragraphs": paragraphs, "sentences": sentences, "word_count": len(text.split()), "char_count": len(text), } class ForensicAnalyzer: RISK_PATTERNS = { "denial_trap": r"\b(deny|denies|denied|denial|no such|never|not applicable)\b", "admission_by_silence": r"\b(fail|failed|failure|omit|omitted|omission|no response|no reply)\b", "procedural_hedging": r"\b(without prejudice|subject to|conditional|provided that|notwithstanding)\b", "burden_flip": r"\b(claimant must|burden|onus|responsibility|incumbent upon|required to prove)\b", } @staticmethod def find_duplicates(docs: List[Dict]) -> List[Dict]: duplicates = [] for i, doc1 in enumerate(docs): for j, doc2 in enumerate(docs[i + 1:], start=i + 1): for para1 in doc1["paragraphs"]: for para2 in doc2["paragraphs"]: if len(para1) < 20 or len(para2) < 20: continue ratio = SequenceMatcher(None, para1, para2).ratio() if ratio > 0.85: duplicates.append({ "doc1": i, "doc2": j, "similarity": ratio, "text": para1[:5000] + "...", "type": "duplicate" if ratio > 0.95 else "near_duplicate", }) return duplicates @staticmethod def find_unique_content(docs: List[Dict]) -> List[Dict]: unique_items = [] for i, doc in enumerate(docs): for para in doc["paragraphs"]: if len(para) < 20: continue is_unique = True for j, other_doc in enumerate(docs): if i == j: continue for other_para in other_doc["paragraphs"]: if SequenceMatcher(None, para, other_para).ratio() > 0.8: is_unique = False break if not is_unique: break if is_unique: unique_items.append({ "doc_index": i, "content": para, "length": len(para), "type": "unique_paragraph", }) return unique_items @staticmethod def detect_risk_language(text: str) -> List[Dict]: findings = [] for risk_type, pattern in ForensicAnalyzer.RISK_PATTERNS.items(): for match in re.finditer(pattern, text, re.IGNORECASE): start = max(0, match.start() - 50) end = min(len(text), match.end() + 50) findings.append({ "type": risk_type, "matched_text": match.group(), "context": text[start:end], "position": match.start(), }) return findings @staticmethod def analyze_documents(docs: List[Dict]) -> Dict: return { "duplicates": ForensicAnalyzer.find_duplicates(docs), "unique_content": ForensicAnalyzer.find_unique_content(docs), "risk_language": [ item for doc in docs for item in ForensicAnalyzer.detect_risk_language(doc["full_text"]) ], } class DocumentMerger: @staticmethod def select_best_content(duplicates, docs): best_versions = [] processed = set() for duplicate in duplicates: key = tuple(sorted([duplicate["doc1"], duplicate["doc2"]])) if key in processed: continue processed.add(key) doc1_text = docs[duplicate["doc1"]]["full_text"] doc2_text = docs[duplicate["doc2"]]["full_text"] best_versions.append(doc1_text if len(doc1_text) > len(doc2_text) else doc2_text) return best_versions @staticmethod def merge_documents(docs, analysis): output = [] output.append("MASTER EVIDENCE DOCUMENT") output.append("=" * 80) output.append(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") output.append(f"Source Documents: {len(docs)}") output.append("=" * 80 + "\n") output.append("EXECUTIVE SUMMARY") output.append("-" * 80) output.append(f"Total Duplicates Found: {len(analysis['duplicates'])}") output.append(f"Unique Content Items: {len(analysis['unique_content'])}") output.append(f"Risk Language Instances: {len(analysis['risk_language'])}\n") output.append("CONSOLIDATED CONTENT") output.append("-" * 80) best_versions = DocumentMerger.select_best_content(analysis["duplicates"], docs) if best_versions: for i, content in enumerate(best_versions, 1): output.append(f"\n[Section {i}]") output.append(content[:50000] + "..." if len(content) > 50000 else content) else: output.append("No duplicate content found across documents.") output.append("\n\nUNIQUE FINDINGS") output.append("-" * 80) for item in analysis["unique_content"][:100]: output.append(f"\n[From Document {item['doc_index'] + 1}]") content = item["content"] output.append(content[:50000] + "..." if len(content) > 50000 else content) output.append("\n\nRISK LANGUAGE ANALYSIS") output.append("-" * 80) risk_summary = {} for risk in analysis["risk_language"]: risk_summary[risk["type"]] = risk_summary.get(risk["type"], 0) + 1 if risk_summary: for risk_type, count in risk_summary.items(): output.append(f"{risk_type.replace('_', ' ').title()}: {count} instances") else: output.append("No risk language patterns detected.") output.append("\n" + "=" * 80) output.append("END OF MASTER EVIDENCE DOCUMENT") return "\n".join(output) def ai_summarise(merged_doc: str, analysis: Dict, hf_token_input=None) -> str: try: risk_summary = {} for risk in analysis.get("risk_language", []): risk_summary[risk["type"]] = risk_summary.get(risk["type"], 0) + 1 input_text = f""" Forensic document analysis results: - Duplicates found: {len(analysis.get('duplicates', []))} - Unique content items: {len(analysis.get('unique_content', []))} - Risk language: {json.dumps(risk_summary)} Document excerpt: {merged_doc[:20000]} """.strip() summary = call_llama( "Summarise this forensic analysis for a litigant in person. " "Be concrete and cite key risks.\n\n" + input_text, user_token=hf_token_input, max_tokens=500 ) risk_lines = "\n".join( [f"- {k.replace('_', ' ').title()}: {v} instances" for k, v in risk_summary.items()] ) or "- None detected" if summary.startswith("Model error:"): return summary return f"AI SUMMARY\n{'=' * 60}\n\n{summary}\n\nRisk Language Breakdown:\n{risk_lines}" except Exception as e: return f"Summarisation error: {str(e)}" def export_txt(merged_doc: str, analysis_report: str) -> str: try: tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".txt", mode="w", encoding="utf-8") tmp.write("SEAMSTRESSES FRAMEWORK — EXPORT\n") tmp.write(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") tmp.write("=" * 80 + "\n\n") tmp.write(merged_doc) tmp.write("\n\n" + "=" * 80 + "\n\n") tmp.write(analysis_report) tmp.close() return tmp.name except Exception: return None def export_pdf(merged_doc: str, analysis_report: str) -> str: try: from reportlab.lib.pagesizes import A4 from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle from reportlab.lib.units import mm from reportlab.lib import colors from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, HRFlowable from reportlab.lib.enums import TA_CENTER tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") tmp.close() doc = SimpleDocTemplate( tmp.name, pagesize=A4, rightMargin=20 * mm, leftMargin=20 * mm, topMargin=20 * mm, bottomMargin=20 * mm ) styles = getSampleStyleSheet() title_style = ParagraphStyle("Title", parent=styles["Heading1"], fontSize=16, alignment=TA_CENTER, spaceAfter=6) heading_style = ParagraphStyle( "Heading", parent=styles["Heading2"], fontSize=11, textColor=colors.HexColor("#1a1a2e"), spaceAfter=4 ) body_style = ParagraphStyle("Body", parent=styles["Normal"], fontSize=9, leading=14, spaceAfter=3) story = [] story.append(Paragraph("Seamstresses Framework", title_style)) story.append(Paragraph("Forensic Document Analysis Report", heading_style)) story.append(Paragraph(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}", body_style)) story.append(HRFlowable(width="100%", thickness=1, color=colors.grey)) story.append(Spacer(1, 10)) story.append(Paragraph("Master Document", heading_style)) for line in merged_doc.split("\n"): if line.strip(): safe = line.replace("&", "&").replace("<", "<").replace(">", ">") if line.startswith("===") or line.startswith("---"): story.append(HRFlowable(width="100%", thickness=0.5, color=colors.lightgrey)) elif line.isupper() and len(line) > 3 and not line.startswith("["): story.append(Paragraph(safe, heading_style)) else: story.append(Paragraph(safe, body_style)) story.append(Spacer(1, 12)) story.append(Paragraph("Analysis Report", heading_style)) story.append(HRFlowable(width="100%", thickness=0.5, color=colors.lightgrey)) for line in analysis_report.split("\n"): if line.strip(): safe = line.replace("&", "&").replace("<", "<").replace(">", ">") if line.isupper() and len(line) > 3: story.append(Paragraph(safe, heading_style)) else: story.append(Paragraph(safe, body_style)) doc.build(story) return tmp.name except Exception as e: print(f"PDF export error: {e}") return None def export_docx(merged_doc: str, analysis_report: str, summary: str) -> str: try: tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".docx") tmp.close() doc = DocxDocument() title = doc.add_heading("Seamstresses Framework", 0) title.alignment = WD_ALIGN_PARAGRAPH.CENTER sub = doc.add_heading("Forensic Document Analysis Report", level=2) sub.alignment = WD_ALIGN_PARAGRAPH.CENTER doc.add_paragraph(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") doc.add_paragraph() if summary: doc.add_heading("AI Summary", level=1) doc.add_paragraph(summary) doc.add_paragraph() doc.add_heading("Master Document", level=1) for line in merged_doc.split("\n"): if line.strip(): if line.startswith("===") or line.startswith("---"): doc.add_paragraph("─" * 60) elif line.isupper() and len(line) > 3 and not line.startswith("["): doc.add_heading(line, level=2) else: doc.add_paragraph(line) doc.add_page_break() doc.add_heading("Analysis Report", level=1) for line in analysis_report.split("\n"): if line.strip(): if line.isupper() and len(line) > 3: doc.add_heading(line, level=2) else: doc.add_paragraph(line) doc.save(tmp.name) return tmp.name except Exception as e: print(f"DOCX export error: {e}") return None def process_documents(file1, file2, file3): try: raw_inputs = [file1, file2, file3] files = [] for uploaded in raw_inputs: if uploaded is None: continue if isinstance(uploaded, dict): path = uploaded.get("path") or uploaded.get("name") elif hasattr(uploaded, "name"): path = uploaded.name elif isinstance(uploaded, str): path = uploaded else: path = str(uploaded) if path: files.append(path) if not files: return "Please upload at least one document.", "", "", None, None, None docs = [] file_names = [] for file_path in files: text = DocumentParser.extract_text_from_file(file_path) if not text or text.startswith("Error"): return f"Error: {text}", "", "", None, None, None docs.append(DocumentParser.parse_document(text)) file_names.append(os.path.basename(file_path)) analysis = ForensicAnalyzer.analyze_documents(docs) merged_doc = DocumentMerger.merge_documents(docs, analysis) analysis_report = f"FORENSIC ANALYSIS REPORT\n{'=' * 80}\n\n" analysis_report += f"FILES ANALYZED: {', '.join(file_names)}\n\n" analysis_report += f"DUPLICATE ANALYSIS\n{'-' * 80}\n" analysis_report += f"Total Duplicates Found: {len(analysis['duplicates'])}\n\n" for duplicate in analysis["duplicates"][:50]: analysis_report += ( f"- Documents {duplicate['doc1'] + 1} & {duplicate['doc2'] + 1}: " f"{duplicate['similarity']:.1%} similarity\n" ) analysis_report += f"\nUNIQUE CONTENT\n{'-' * 80}\n" analysis_report += f"Total Unique Items: {len(analysis['unique_content'])}\n\n" for item in analysis["unique_content"][:50]: analysis_report += f"- From Document {item['doc_index'] + 1}: {item['content'][:5000]}...\n" analysis_report += f"\nRISK LANGUAGE DETECTED\n{'-' * 80}\n" risk_types = {} for risk in analysis["risk_language"]: risk_types[risk["type"]] = risk_types.get(risk["type"], 0) + 1 if risk_types: for risk_type, count in risk_types.items(): analysis_report += f"- {risk_type.replace('_', ' ').title()}: {count} instances\n" else: analysis_report += "No risk language patterns detected.\n" summary = ai_summarise(merged_doc, analysis) txt_path = export_txt(merged_doc, analysis_report) pdf_path = export_pdf(merged_doc, analysis_report) docx_path = export_docx(merged_doc, analysis_report, summary) return merged_doc, analysis_report, summary, txt_path, pdf_path, docx_path except Exception as e: import traceback return f"Error: {str(e)}\n\n{traceback.format_exc()}", "", "", None, None, None # ========================= # UI # ========================= CSS = """ :root { --bg: #030712; --surface: #0a0f1e; --surface2: #0f1629; --border: #1a2544; --accent: #00CFFF; --accent2: #FFE500; --accent3: #0066FF; --text: #E8F4FF; --muted: #8AA0C0; --r: 8px; } body, .gradio-container { background: var(--bg) !important; color: var(--text) !important; } .hdr { text-align: center; padding: 2.4rem 1rem 1.6rem; border-bottom: 1px solid var(--border); } .hdr h1 { font-size: 2.2rem; font-weight: 800; color: var(--accent); margin: 0 0 0.3rem; } .hdr .sub { color: var(--muted); font-size: .75rem; letter-spacing: .06em; } .hdr .own { color: var(--accent2); font-size: .68rem; margin-top: .3rem; letter-spacing: .1em; text-transform: uppercase; } textarea, input[type=text] { background: var(--surface) !important; border: 1px solid var(--border) !important; border-radius: var(--r) !important; color: var(--text) !important; } button.primary { background: linear-gradient(135deg, var(--accent3), var(--accent)) !important; color: #030712 !important; border: none !important; border-radius: var(--r) !important; font-weight: 800 !important; } button.secondary { background: var(--surface2) !important; color: var(--accent2) !important; border: 1px solid var(--accent3) !important; border-radius: var(--r) !important; font-weight: 700 !important; } .out textarea { background: #020510 !important; border-left: 3px solid var(--accent) !important; font-size: .84rem !important; line-height: 1.7 !important; } .foot { text-align: center; padding: 1.5rem; color: var(--muted); font-size: .68rem; border-top: 1px solid var(--border); margin-top: 2rem; } """ with gr.Blocks(css=CSS, title="The Metamorphic Phoenix") as demo: gr.HTML( """
Metabolic Phoenix · Seamstresses Framework · Injectiv8 · Ms. Nala
Invented & Owned by Dwayne Anthony Brian Galloway · 2026