Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -202,504 +202,175 @@ def analyse(files):
|
|
| 202 |
return r1, r2, r3, r4
|
| 203 |
|
| 204 |
|
| 205 |
-
# ──
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
def text_to_speech_full(text: str, speed: float = 1.0, pitch: float = 1.0) -> str | None:
|
| 216 |
-
"""
|
| 217 |
-
Full-length neural TTS — Microsoft Sonia (en-GB neural voice).
|
| 218 |
-
No length cap. speed: 0.5–2.0. pitch: 0.5–2.0.
|
| 219 |
-
"""
|
| 220 |
-
if not text or not text.strip():
|
| 221 |
-
return None
|
| 222 |
-
|
| 223 |
-
text = text.strip()
|
| 224 |
-
import re
|
| 225 |
-
sentences = re.split(r'(?<=[.!?])\s+', text)
|
| 226 |
-
|
| 227 |
-
chunks = []
|
| 228 |
-
current = ""
|
| 229 |
-
for s in sentences:
|
| 230 |
-
if len(current) + len(s) + 1 <= CHUNK_SIZE:
|
| 231 |
-
current = (current + " " + s).strip()
|
| 232 |
-
else:
|
| 233 |
-
if current:
|
| 234 |
-
chunks.append(current)
|
| 235 |
-
if len(s) > CHUNK_SIZE:
|
| 236 |
-
words = s.split()
|
| 237 |
-
wchunk = ""
|
| 238 |
-
for w in words:
|
| 239 |
-
if len(wchunk) + len(w) + 1 <= CHUNK_SIZE:
|
| 240 |
-
wchunk = (wchunk + " " + w).strip()
|
| 241 |
-
else:
|
| 242 |
-
if wchunk:
|
| 243 |
-
chunks.append(wchunk)
|
| 244 |
-
wchunk = w
|
| 245 |
-
if wchunk:
|
| 246 |
-
chunks.append(wchunk)
|
| 247 |
-
else:
|
| 248 |
-
current = s
|
| 249 |
-
if current:
|
| 250 |
-
chunks.append(current)
|
| 251 |
-
|
| 252 |
-
if not chunks:
|
| 253 |
-
return None
|
| 254 |
-
|
| 255 |
-
tmp_files = []
|
| 256 |
-
try:
|
| 257 |
-
loop = asyncio.new_event_loop()
|
| 258 |
-
asyncio.set_event_loop(loop)
|
| 259 |
-
for i, chunk in enumerate(chunks):
|
| 260 |
-
out_tmp = tempfile.NamedTemporaryFile(delete=False, suffix=f"_chunk{i}.mp3", dir="/tmp")
|
| 261 |
-
out_tmp.close()
|
| 262 |
-
try:
|
| 263 |
-
loop.run_until_complete(_speak_chunk(chunk, out_tmp.name))
|
| 264 |
-
tmp_files.append(out_tmp.name)
|
| 265 |
-
except Exception:
|
| 266 |
-
try: os.unlink(out_tmp.name)
|
| 267 |
-
except Exception: pass
|
| 268 |
-
loop.close()
|
| 269 |
-
|
| 270 |
-
if not tmp_files:
|
| 271 |
-
return None
|
| 272 |
-
|
| 273 |
-
combined = AudioSegment.empty()
|
| 274 |
-
pause = AudioSegment.silent(duration=150)
|
| 275 |
-
for f in tmp_files:
|
| 276 |
-
try:
|
| 277 |
-
combined += AudioSegment.from_mp3(f) + pause
|
| 278 |
-
except Exception:
|
| 279 |
-
pass
|
| 280 |
-
|
| 281 |
-
if pitch != 1.0:
|
| 282 |
-
combined = combined._spawn(
|
| 283 |
-
combined.raw_data,
|
| 284 |
-
overrides={"frame_rate": int(combined.frame_rate * pitch)}
|
| 285 |
-
).set_frame_rate(44100)
|
| 286 |
-
|
| 287 |
-
if speed != 1.0:
|
| 288 |
-
combined = combined._spawn(
|
| 289 |
-
combined.raw_data,
|
| 290 |
-
overrides={"frame_rate": int(combined.frame_rate * speed)}
|
| 291 |
-
).set_frame_rate(44100)
|
| 292 |
-
|
| 293 |
-
out = tempfile.NamedTemporaryFile(delete=False, suffix=".mp3", dir="/tmp", prefix="mpf_tts_")
|
| 294 |
-
combined.export(out.name, format="mp3")
|
| 295 |
-
out.close()
|
| 296 |
-
|
| 297 |
-
for f in tmp_files:
|
| 298 |
-
try: os.unlink(f)
|
| 299 |
-
except Exception: pass
|
| 300 |
|
| 301 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 302 |
|
| 303 |
-
except Exception:
|
| 304 |
-
return tmp_files[0] if tmp_files else None
|
| 305 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 306 |
|
| 307 |
-
def make_audio(text, speed=1.0, pitch=1.0):
|
| 308 |
-
return text_to_speech_full(text, speed=speed, pitch=pitch)
|
| 309 |
|
| 310 |
-
def
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 314 |
|
| 315 |
-
def read_all(t1, t2, t3, t4, sp, pi):
|
| 316 |
-
combined = "\n\n".join([x for x in [t1, t2, t3, t4] if x and x.strip()])
|
| 317 |
-
return make_audio(combined, sp, pi)
|
| 318 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 319 |
|
| 320 |
-
def read_document_only(files, speed, pitch):
|
| 321 |
-
"""Extract all content from uploaded files and read it aloud — no analysis."""
|
| 322 |
-
if not files:
|
| 323 |
-
return None, "⚠️ Please upload at least one file."
|
| 324 |
-
if not isinstance(files, list):
|
| 325 |
-
files = [files]
|
| 326 |
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 333 |
|
| 334 |
-
if not all_content:
|
| 335 |
-
return None, "⚠️ Could not extract content from the uploaded files."
|
| 336 |
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
word_count = len(full_text.split())
|
| 340 |
-
char_count = len(full_text)
|
| 341 |
-
msg = f"✅ Reading {len(files)} file{'s' if len(files)>1 else ''} · {word_count:,} words · {char_count:,} characters"
|
| 342 |
-
return audio_path, msg
|
| 343 |
-
def save_analysis(r1, r2, r3, r4):
|
| 344 |
if not r1:
|
| 345 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 346 |
ts = time.strftime("%Y-%m-%d %H:%M")
|
| 347 |
content = (
|
| 348 |
-
f"METABOLIC PHOENIX FRAMEWORK —
|
| 349 |
f"Generated: {ts}\n"
|
| 350 |
-
f"
|
| 351 |
-
f"{'='*60}\n\
|
| 352 |
-
f"{'='*60}\n\
|
| 353 |
-
f"{'='*60}\n\
|
| 354 |
-
f"{'='*60}\n\
|
|
|
|
| 355 |
)
|
| 356 |
tmp = tempfile.NamedTemporaryFile(
|
| 357 |
delete=False, suffix=".txt",
|
| 358 |
-
prefix=f"
|
| 359 |
mode="w", encoding="utf-8"
|
| 360 |
)
|
| 361 |
tmp.write(content)
|
| 362 |
tmp.close()
|
| 363 |
-
return tmp.name, "✅
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
# ── CSS ───────────────────────────────────────────────────────────────────
|
| 367 |
-
CSS = """
|
| 368 |
-
@import url('https://fonts.googleapis.com/css2?family=Syne:wght@400;700;800&family=DM+Mono:wght@400;500&display=swap');
|
| 369 |
-
:root {
|
| 370 |
-
--bg: #030712; --surface: #0a0f1e; --surface2: #061a0f;
|
| 371 |
-
--border: #1a2544; --accent: #00CFFF; --accent2: #FFE500;
|
| 372 |
-
--accent3: #0066FF; --text: #E8F4FF; --muted: #4A6080; --r: 8px;
|
| 373 |
-
--green: #00e676; --green-dim: rgba(0,230,118,0.08); --green-border: rgba(0,230,118,0.3);
|
| 374 |
-
}
|
| 375 |
-
body, .gradio-container { background:var(--bg)!important; font-family:'Syne',sans-serif!important; color:var(--text)!important; }
|
| 376 |
-
.hdr { text-align:center; padding:2.4rem 1rem 1.6rem; border-bottom:1px solid var(--border);
|
| 377 |
-
background: radial-gradient(ellipse at 30% 50%, rgba(0,102,255,0.12) 0%, transparent 60%),
|
| 378 |
-
radial-gradient(ellipse at 70% 50%, rgba(0,207,255,0.08) 0%, transparent 60%); }
|
| 379 |
-
.hdr h1 { font-size:2.2rem; font-weight:800; letter-spacing:-0.04em;
|
| 380 |
-
background:linear-gradient(135deg,var(--accent) 0%,var(--accent2) 100%);
|
| 381 |
-
-webkit-background-clip:text; -webkit-text-fill-color:transparent; margin:0 0 0.3rem; }
|
| 382 |
-
.hdr .sub { color:var(--muted); font-family:'DM Mono',monospace; font-size:.75rem; letter-spacing:.06em; }
|
| 383 |
-
.hdr .own { color:var(--accent2); font-family:'DM Mono',monospace; font-size:.68rem; margin-top:.3rem; letter-spacing:.1em; text-transform:uppercase; }
|
| 384 |
-
input[type=text], textarea { background:#041a0c!important; border:1px solid var(--green-border)!important;
|
| 385 |
-
border-radius:var(--r)!important; color:#ccffe6!important; font-family:'DM Mono',monospace!important;
|
| 386 |
-
font-size:.86rem!important; padding:.6rem .85rem!important; transition:border-color .18s!important; }
|
| 387 |
-
input[type=text]:focus, textarea:focus { border-color:var(--green)!important; outline:none!important;
|
| 388 |
-
box-shadow:0 0 0 3px rgba(0,230,118,.15)!important; }
|
| 389 |
-
label > span:first-child { color:var(--green)!important; font-family:'DM Mono',monospace!important;
|
| 390 |
-
font-size:.7rem!important; letter-spacing:.08em!important; text-transform:uppercase!important; }
|
| 391 |
-
button.primary { background:linear-gradient(135deg,#006600,var(--green))!important;
|
| 392 |
-
color:#030712!important; border:none!important; border-radius:var(--r)!important;
|
| 393 |
-
font-family:'Syne',sans-serif!important; font-weight:800!important; font-size:.92rem!important;
|
| 394 |
-
padding:.75rem 2.8rem!important; cursor:pointer!important; transition:opacity .15s,transform .15s!important; }
|
| 395 |
-
button.primary:hover { opacity:.88!important; transform:translateY(-1px)!important; }
|
| 396 |
-
button.secondary { background:#041a0c!important; color:var(--green)!important;
|
| 397 |
-
border:1px solid var(--green-border)!important; border-radius:var(--r)!important;
|
| 398 |
-
font-family:'Syne',sans-serif!important; font-weight:700!important; font-size:.8rem!important;
|
| 399 |
-
padding:.5rem 1.2rem!important; cursor:pointer!important; }
|
| 400 |
-
button.secondary:hover { background:var(--green)!important; color:#030712!important; }
|
| 401 |
-
.out textarea { background:#020e06!important; border:1px solid var(--green-border)!important;
|
| 402 |
-
border-left:3px solid var(--green)!important; border-radius:0 var(--r) var(--r) 0!important;
|
| 403 |
-
font-family:'DM Mono',monospace!important; font-size:.84rem!important; line-height:1.7!important;
|
| 404 |
-
color:#ccffe6!important; }
|
| 405 |
-
.gradio-accordion { background:#061a0f!important; border:1px solid var(--green-border)!important;
|
| 406 |
-
border-radius:var(--r)!important; margin-bottom:.6rem!important; }
|
| 407 |
-
.gradio-accordion .label-wrap span { color:var(--green)!important; font-family:'Syne',sans-serif!important;
|
| 408 |
-
font-weight:700!important; font-size:.85rem!important; letter-spacing:.04em!important; }
|
| 409 |
-
.gradio-accordion svg { color:var(--green)!important; stroke:var(--green)!important; }
|
| 410 |
-
.foot { text-align:center; padding:1.5rem; color:var(--muted); font-family:'DM Mono',monospace;
|
| 411 |
-
font-size:.68rem; border-top:1px solid var(--green-border); margin-top:2rem; letter-spacing:.05em; }
|
| 412 |
-
"""
|
| 413 |
-
|
| 414 |
-
# ── UI ────────────────────────────────────────────────────────────────────
|
| 415 |
-
with gr.Blocks(css=CSS, title="Metabolic Phoenix Framework") as demo:
|
| 416 |
-
|
| 417 |
-
gr.HTML("""
|
| 418 |
-
<div class="hdr">
|
| 419 |
-
<h1>⚡ The Metabolic Phoenix Framework</h1>
|
| 420 |
-
<p class="sub">Injectiv8 · Ms. Nala · Llama-3.3-70B</p>
|
| 421 |
-
<p class="own">Invented & Owned by Dwayne Anthony Brian Galloway · 2026</p>
|
| 422 |
-
</div>
|
| 423 |
-
""")
|
| 424 |
-
|
| 425 |
-
pdf_input = gr.File(
|
| 426 |
-
label="Upload Documents — drop multiple files — PDF · Image · Audio · Video · Text",
|
| 427 |
-
file_types=[
|
| 428 |
-
".pdf", ".txt", ".md",
|
| 429 |
-
".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".tiff",
|
| 430 |
-
".mp3", ".wav", ".m4a", ".ogg", ".flac",
|
| 431 |
-
"audio/mpeg", "audio/mp3", "audio/wav", "audio/x-wav",
|
| 432 |
-
"audio/mp4", "audio/ogg", "audio/flac", "audio/*",
|
| 433 |
-
".mp4", ".mov", ".avi", ".mkv", ".webm",
|
| 434 |
-
"video/mp4", "video/quicktime", "video/x-msvideo",
|
| 435 |
-
"video/x-matroska", "video/webm", "video/*",
|
| 436 |
-
],
|
| 437 |
-
file_count="multiple",
|
| 438 |
-
type="filepath"
|
| 439 |
-
)
|
| 440 |
-
run_btn = gr.Button("⚡ Analyse Documents", variant="primary")
|
| 441 |
-
|
| 442 |
-
with gr.Row():
|
| 443 |
-
speed_slider = gr.Slider(
|
| 444 |
-
minimum=0.5, maximum=2.0, value=1.0, step=0.05,
|
| 445 |
-
label="🎚 Speed", info="0.5 = slow · 1.0 = normal · 2.0 = fast"
|
| 446 |
-
)
|
| 447 |
-
pitch_slider = gr.Slider(
|
| 448 |
-
minimum=0.5, maximum=2.0, value=1.0, step=0.05,
|
| 449 |
-
label="🎵 Pitch", info="0.5 = low · 1.0 = normal · 2.0 = high"
|
| 450 |
-
)
|
| 451 |
-
|
| 452 |
-
with gr.Accordion("🔊 Just Read The Document — no analysis", open=True):
|
| 453 |
-
read_doc_btn = gr.Button("🔊 Read Document(s) Aloud — full length, no analysis", variant="secondary")
|
| 454 |
-
read_doc_msg = gr.Markdown("")
|
| 455 |
-
read_doc_audio = gr.Audio(label="Document Audio", autoplay=True)
|
| 456 |
-
|
| 457 |
-
with gr.Accordion("Cycle 1 — Fact Ledger", open=True):
|
| 458 |
-
out1 = gr.Textbox(label="", lines=12, elem_classes=["out"])
|
| 459 |
-
rb1_btn = gr.Button("🔊 Read Cycle 1 (full)", variant="secondary")
|
| 460 |
-
audio1 = gr.Audio(label="Cycle 1 Audio", autoplay=True)
|
| 461 |
-
|
| 462 |
-
with gr.Accordion("Cycle 2 — Compliance Analysis", open=False):
|
| 463 |
-
out2 = gr.Textbox(label="", lines=12, elem_classes=["out"])
|
| 464 |
-
rb2_btn = gr.Button("🔊 Read Cycle 2 (full)", variant="secondary")
|
| 465 |
-
audio2 = gr.Audio(label="Cycle 2 Audio", autoplay=True)
|
| 466 |
-
|
| 467 |
-
with gr.Accordion("Cycle 3 — Ouroboros Loops", open=False):
|
| 468 |
-
out3 = gr.Textbox(label="", lines=12, elem_classes=["out"])
|
| 469 |
-
rb3_btn = gr.Button("🔊 Read Cycle 3 (full)", variant="secondary")
|
| 470 |
-
audio3 = gr.Audio(label="Cycle 3 Audio", autoplay=True)
|
| 471 |
-
|
| 472 |
-
with gr.Accordion("Cycle 4 — Full Case Output", open=False):
|
| 473 |
-
out4 = gr.Textbox(label="", lines=12, elem_classes=["out"])
|
| 474 |
-
rb4_btn = gr.Button("🔊 Read Cycle 4 (full)", variant="secondary")
|
| 475 |
-
audio4 = gr.Audio(label="Cycle 4 Audio", autoplay=True)
|
| 476 |
-
|
| 477 |
-
rball_btn = gr.Button("🔊 Read Full Analysis (all cycles)", variant="primary")
|
| 478 |
-
audio_all = gr.Audio(label="Full Analysis Audio", autoplay=True)
|
| 479 |
-
|
| 480 |
-
save_btn = gr.Button("💾 Save & Download Analysis", variant="secondary")
|
| 481 |
-
dl_file = gr.File(label="Download your analysis")
|
| 482 |
-
save_msg = gr.Markdown("")
|
| 483 |
-
|
| 484 |
-
run_btn.click(analyse, inputs=[pdf_input], outputs=[out1, out2, out3, out4])
|
| 485 |
-
read_doc_btn.click(read_document_only, inputs=[pdf_input, speed_slider, pitch_slider], outputs=[read_doc_audio, read_doc_msg])
|
| 486 |
-
rb1_btn.click(read_cycle1, inputs=[out1, speed_slider, pitch_slider], outputs=[audio1])
|
| 487 |
-
rb2_btn.click(read_cycle2, inputs=[out2, speed_slider, pitch_slider], outputs=[audio2])
|
| 488 |
-
rb3_btn.click(read_cycle3, inputs=[out3, speed_slider, pitch_slider], outputs=[audio3])
|
| 489 |
-
rb4_btn.click(read_cycle4, inputs=[out4, speed_slider, pitch_slider], outputs=[audio4])
|
| 490 |
-
rball_btn.click(read_all, inputs=[out1, out2, out3, out4, speed_slider, pitch_slider], outputs=[audio_all])
|
| 491 |
-
save_btn.click(save_analysis, inputs=[out1, out2, out3, out4], outputs=[dl_file, save_msg])
|
| 492 |
-
|
| 493 |
-
gr.HTML("""
|
| 494 |
-
<div class="foot">
|
| 495 |
-
THE METABOLIC PHOENIX FRAMEWORK · Injectiv8 · Ms. Nala
|
| 496 |
-
· © Dwayne Anthony Brian Galloway 2026 · All Rights Reserved
|
| 497 |
-
</div>
|
| 498 |
-
""")
|
| 499 |
-
|
| 500 |
-
if __name__ == "__main__":
|
| 501 |
-
demo.launch()import gradio as gr
|
| 502 |
-
import os
|
| 503 |
-
import time
|
| 504 |
-
import subprocess
|
| 505 |
-
import tempfile
|
| 506 |
-
import math
|
| 507 |
-
from pathlib import Path
|
| 508 |
-
from huggingface_hub import InferenceClient
|
| 509 |
-
|
| 510 |
-
# ── INSTALL DEPENDENCIES ────────────────────────────────────────────────
|
| 511 |
-
for pkg in ["pypdf", "edge-tts", "pydub", "pillow", "pytesseract", "openai-whisper", "ffmpeg-python"]:
|
| 512 |
-
subprocess.run(["pip", "install", pkg, "-q"], capture_output=True)
|
| 513 |
-
|
| 514 |
-
# Install ffmpeg system binary if not present
|
| 515 |
-
subprocess.run(["apt-get", "install", "-y", "-q", "ffmpeg", "tesseract-ocr"], capture_output=True)
|
| 516 |
-
|
| 517 |
-
from pypdf import PdfReader
|
| 518 |
-
import asyncio
|
| 519 |
-
import edge_tts
|
| 520 |
-
from pydub import AudioSegment
|
| 521 |
-
from PIL import Image
|
| 522 |
-
import pytesseract
|
| 523 |
-
|
| 524 |
-
MODEL = "meta-llama/Llama-3.3-70B-Instruct"
|
| 525 |
-
|
| 526 |
-
# ── CLIENT ───────────────────────────────────────────────────────────────
|
| 527 |
-
def get_client():
|
| 528 |
-
token = os.environ.get("HF_TOKEN", "")
|
| 529 |
-
return InferenceClient(model=MODEL, token=token if token else None)
|
| 530 |
-
|
| 531 |
-
|
| 532 |
-
# ── FILE EXTRACTION ───────────────────────────────────────────────────────
|
| 533 |
-
def extract_pdf_text(path):
|
| 534 |
-
try:
|
| 535 |
-
reader = PdfReader(path)
|
| 536 |
-
pages = []
|
| 537 |
-
for i, page in enumerate(reader.pages):
|
| 538 |
-
text = page.extract_text() or ""
|
| 539 |
-
if text.strip():
|
| 540 |
-
pages.append(f"--- Page {i+1} ---\n{text.strip()}")
|
| 541 |
-
if not pages:
|
| 542 |
-
return "[No extractable text — may be a scanned PDF]"
|
| 543 |
-
return "\n\n".join(pages)
|
| 544 |
-
except Exception as e:
|
| 545 |
-
return f"[PDF read error: {e}]"
|
| 546 |
-
|
| 547 |
-
|
| 548 |
-
def extract_image_text(path):
|
| 549 |
-
"""OCR an image using pytesseract."""
|
| 550 |
-
try:
|
| 551 |
-
img = Image.open(path)
|
| 552 |
-
text = pytesseract.image_to_string(img, lang="eng")
|
| 553 |
-
if text.strip():
|
| 554 |
-
return f"[IMAGE OCR]\n{text.strip()}"
|
| 555 |
-
return "[Image: no text detected by OCR]"
|
| 556 |
-
except Exception as e:
|
| 557 |
-
return f"[Image OCR error: {e}]"
|
| 558 |
-
|
| 559 |
-
|
| 560 |
-
def transcribe_audio(path):
|
| 561 |
-
"""Transcribe audio/video using OpenAI Whisper (runs locally)."""
|
| 562 |
-
try:
|
| 563 |
-
import whisper
|
| 564 |
-
model = whisper.load_model("base")
|
| 565 |
-
result = model.transcribe(path, language="en")
|
| 566 |
-
return f"[AUDIO TRANSCRIPTION]\n{result['text'].strip()}"
|
| 567 |
-
except Exception as e:
|
| 568 |
-
return f"[Transcription error: {e}]"
|
| 569 |
-
|
| 570 |
-
|
| 571 |
-
def extract_content(file):
|
| 572 |
-
"""Route to correct extractor based on file type."""
|
| 573 |
-
if not file:
|
| 574 |
-
return ""
|
| 575 |
-
path = file.name if hasattr(file, "name") else str(file)
|
| 576 |
-
ext = Path(path).suffix.lower()
|
| 577 |
-
|
| 578 |
-
if ext == ".pdf":
|
| 579 |
-
return extract_pdf_text(path)
|
| 580 |
-
elif ext in [".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".tiff"]:
|
| 581 |
-
return extract_image_text(path)
|
| 582 |
-
elif ext in [".mp3", ".wav", ".ogg", ".m4a", ".flac"]:
|
| 583 |
-
return transcribe_audio(path)
|
| 584 |
-
elif ext in [".mp4", ".mov", ".avi", ".mkv", ".webm"]:
|
| 585 |
-
# Extract audio from video then transcribe
|
| 586 |
-
try:
|
| 587 |
-
tmp_audio = tempfile.NamedTemporaryFile(suffix=".mp3", delete=False, dir="/tmp")
|
| 588 |
-
subprocess.run(
|
| 589 |
-
["ffmpeg", "-i", path, "-q:a", "0", "-map", "a", tmp_audio.name, "-y"],
|
| 590 |
-
capture_output=True, timeout=120
|
| 591 |
-
)
|
| 592 |
-
return transcribe_audio(tmp_audio.name)
|
| 593 |
-
except Exception as e:
|
| 594 |
-
return f"[Video extraction error: {e}]"
|
| 595 |
-
elif ext in [".txt", ".md", ".csv"]:
|
| 596 |
-
try:
|
| 597 |
-
return Path(path).read_text(encoding="utf-8", errors="replace")
|
| 598 |
-
except Exception as e:
|
| 599 |
-
return f"[Text read error: {e}]"
|
| 600 |
-
else:
|
| 601 |
-
try:
|
| 602 |
-
return Path(path).read_text(encoding="utf-8", errors="replace")
|
| 603 |
-
except Exception:
|
| 604 |
-
return f"[Unsupported file type: {ext}]"
|
| 605 |
-
|
| 606 |
-
|
| 607 |
-
# ── AI ────────────────────────────────────────────────────────────────────
|
| 608 |
-
def call_llama(prompt, max_tokens=2000):
|
| 609 |
-
try:
|
| 610 |
-
client = get_client()
|
| 611 |
-
response = client.chat_completion(
|
| 612 |
-
messages=[{"role": "user", "content": prompt}],
|
| 613 |
-
max_tokens=max_tokens,
|
| 614 |
-
temperature=0.7,
|
| 615 |
-
)
|
| 616 |
-
return response.choices[0].message.content
|
| 617 |
-
except Exception as e:
|
| 618 |
-
return f"⚠️ Error: {e}"
|
| 619 |
-
|
| 620 |
-
|
| 621 |
-
# ── ANALYSIS ──────────────────────────────────────────────────────────────
|
| 622 |
-
PLAIN_ENGLISH = (
|
| 623 |
-
"\n\nCRITICAL: Write in plain clear English prose only. "
|
| 624 |
-
"No JSON. No code. No brackets like {{ or }}. No markdown symbols. "
|
| 625 |
-
"No asterisks. No hash symbols. Just clear numbered paragraphs and sentences "
|
| 626 |
-
"a non-lawyer can read and understand."
|
| 627 |
-
)
|
| 628 |
-
|
| 629 |
-
def analyse(files):
|
| 630 |
-
if not files:
|
| 631 |
-
return "⚠️ Please upload at least one file.", "", "", ""
|
| 632 |
-
|
| 633 |
-
# Accept single file or list
|
| 634 |
-
if not isinstance(files, list):
|
| 635 |
-
files = [files]
|
| 636 |
-
|
| 637 |
-
# Extract content from all files and combine
|
| 638 |
-
all_content = []
|
| 639 |
-
for i, f in enumerate(files):
|
| 640 |
-
content = extract_content(f)
|
| 641 |
-
name = Path(f if isinstance(f, str) else f.name).name if f else f"Document {i+1}"
|
| 642 |
-
if content and not content.startswith("["):
|
| 643 |
-
all_content.append(f"DOCUMENT {i+1}: {name}\n{'─'*50}\n{content}")
|
| 644 |
-
else:
|
| 645 |
-
all_content.append(f"DOCUMENT {i+1}: {name}\n{'─'*50}\n{content or '[Empty]'}")
|
| 646 |
-
|
| 647 |
-
combined = "\n\n".join(all_content)
|
| 648 |
-
doc_chunk = combined[:14000]
|
| 649 |
-
base = f"DOCUMENTS SUPPLIED ({len(files)} total):\n\n{doc_chunk}\n\n{'='*60}\n\n"
|
| 650 |
-
|
| 651 |
-
r1 = call_llama(base +
|
| 652 |
-
"You are Introbot — part of the Metabolic Phoenix Framework by Dwayne Anthony Brian Galloway.\n\n"
|
| 653 |
-
"These documents contain evidence of institutional breaches. Your job is not to speculate — it is to record what is confirmed on the record.\n\n"
|
| 654 |
-
"From these documents state with certainty:\n"
|
| 655 |
-
"- Every confirmed fact: who said it, when, and what it proves\n"
|
| 656 |
-
"- Every admission made, explicit or implied — label it ADMISSION\n"
|
| 657 |
-
"- Every denial made — label it DENIAL and state what it contradicts\n"
|
| 658 |
-
"- Every contradiction — state what was said, what contradicts it, and when\n"
|
| 659 |
-
"- Every deliberate omission — what was required to be said, was not said, and why that is a breach\n"
|
| 660 |
-
"- Every statutory duty that was triggered and the precise moment it was triggered\n\n"
|
| 661 |
-
"Do not hedge. Do not say 'may indicate' or 'appears to suggest'. If it is in the document, it happened. State it as fact.\n\n"
|
| 662 |
-
"Format as a clean numbered FACT LEDGER." + PLAIN_ENGLISH
|
| 663 |
-
)
|
| 664 |
-
r2 = call_llama(base +
|
| 665 |
-
"You are RHB 2.0 + CPF — part of the Metabolic Phoenix Framework by Dwayne Anthony Brian Galloway.\n\n"
|
| 666 |
-
"Breaches have occurred. Your role is to name them, prove them, and record them for litigation.\n\n"
|
| 667 |
-
"From these documents confirm the following:\n"
|
| 668 |
-
"- Each institution's statutory duties and the exact moment each duty was triggered\n"
|
| 669 |
-
"- For each duty: state BREACH CONFIRMED or COMPLIED — never leave it neutral\n"
|
| 670 |
-
"- For every breach: name the statute, the section, the precise act or omission, and the date it occurred\n"
|
| 671 |
-
"- Every legal error is to be recorded as a finding, not a possibility\n"
|
| 672 |
-
"- Trace the full causal chain: first breach → escalation → compounded harm → final injury\n"
|
| 673 |
-
"- Equality Act 2010 breaches: confirm each s.13 direct discrimination and s.20 failure to make reasonable adjustments as established — not alleged\n"
|
| 674 |
-
"- For each breach state the remedy the law requires and what has been unlawfully withheld\n\n"
|
| 675 |
-
"Do not use the word 'may', 'potentially', 'appears' or 'suggests'. These are findings, not opinions.\n"
|
| 676 |
-
"Be forensic. Cite statute sections." + PLAIN_ENGLISH
|
| 677 |
-
)
|
| 678 |
-
r3 = call_llama(base +
|
| 679 |
-
"You are Phoenix Badger — part of the Metabolic Phoenix Framework by Dwayne Anthony Brian Galloway.\n\n"
|
| 680 |
-
"Run all four Ouroboros rings on these documents. These rings do not speculate — they expose.\n\n"
|
| 681 |
-
"RING A — FORWARD SCAN: State every evasion tactic, deflection, and bad faith manoeuvre confirmed in these documents. Name what each one is designed to obscure.\n\n"
|
| 682 |
-
"RING B — BACKWARD SWEEP: State every past decision in these documents that was unlawful at the time it was made. Name the law that was breached and when.\n\n"
|
| 683 |
-
"RING C — TYPOLOGY MUTATION: Name the patterns of institutional misconduct these documents confirm. State whether each pattern is systemic or targeted.\n\n"
|
| 684 |
-
"RING D — DRIFT CORRECTION: Call out every internal contradiction and inconsistency across the documents. State what each contradiction proves.\n\n"
|
| 685 |
-
"End with ESCALATION MAP: which regulatory bodies and courts must receive this, in priority order, with reasons." + PLAIN_ENGLISH
|
| 686 |
-
)
|
| 687 |
-
r4 = call_llama(
|
| 688 |
-
f"You are Layoutbot — part of the Metabolic Phoenix Framework by Dwayne Anthony Brian Galloway.\n\n"
|
| 689 |
-
f"DOCUMENT EXTRACT:\n{doc_chunk[:3000]}\n\n"
|
| 690 |
-
f"FACT LEDGER:\n{r1[:1500]}\n\n"
|
| 691 |
-
f"COMPLIANCE ANALYSIS:\n{r2[:1500]}\n\n"
|
| 692 |
-
f"LOOP OUTPUT:\n{r3[:1500]}\n\n"
|
| 693 |
-
f"{'='*60}\n\n"
|
| 694 |
-
"Produce a complete litigation-ready case summary. This is not a risk assessment. Breaches have occurred and are on the record. Write accordingly.\n\n"
|
| 695 |
-
"EXECUTIVE SUMMARY — plain English account of what happened, what was done wrong, and who did it\n"
|
| 696 |
-
"CONFIRMED BREACHES — each breach named, statute and section cited, date of breach confirmed\n"
|
| 697 |
-
"TOP 5 ARGUMENTS — strongest arguments first, each grounded in confirmed fact and statute\n"
|
| 698 |
-
"REQUIRED NEXT STEPS — mandatory actions in priority order, not suggestions\n"
|
| 699 |
-
"ESCALATION MAP — which body receives what, by what deadline, and what the failure to escalate costs\n\n"
|
| 700 |
-
"Ms. Nala | Injectiv8 | Dwayne Anthony Brian Galloway" + PLAIN_ENGLISH
|
| 701 |
-
)
|
| 702 |
-
return r1, r2, r3, r4
|
| 703 |
|
| 704 |
|
| 705 |
# ── FULL TTS — NO 30-SECOND CAP ──────────────────────────────────────────
|
|
@@ -981,6 +652,62 @@ with gr.Blocks(css=CSS, title="Metabolic Phoenix Framework") as demo:
|
|
| 981 |
dl_file = gr.File(label="Download your analysis")
|
| 982 |
save_msg = gr.Markdown("")
|
| 983 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 984 |
run_btn.click(analyse, inputs=[pdf_input], outputs=[out1, out2, out3, out4])
|
| 985 |
read_doc_btn.click(read_document_only, inputs=[pdf_input, speed_slider, pitch_slider], outputs=[read_doc_audio, read_doc_msg])
|
| 986 |
rb1_btn.click(read_cycle1, inputs=[out1, speed_slider, pitch_slider], outputs=[audio1])
|
|
@@ -990,6 +717,22 @@ with gr.Blocks(css=CSS, title="Metabolic Phoenix Framework") as demo:
|
|
| 990 |
rball_btn.click(read_all, inputs=[out1, out2, out3, out4, speed_slider, pitch_slider], outputs=[audio_all])
|
| 991 |
save_btn.click(save_analysis, inputs=[out1, out2, out3, out4], outputs=[dl_file, save_msg])
|
| 992 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 993 |
gr.HTML("""
|
| 994 |
<div class="foot">
|
| 995 |
THE METABOLIC PHOENIX FRAMEWORK · Injectiv8 · Ms. Nala
|
|
@@ -999,4 +742,3 @@ with gr.Blocks(css=CSS, title="Metabolic Phoenix Framework") as demo:
|
|
| 999 |
|
| 1000 |
if __name__ == "__main__":
|
| 1001 |
demo.launch()
|
| 1002 |
-
|
|
|
|
| 202 |
return r1, r2, r3, r4
|
| 203 |
|
| 204 |
|
| 205 |
+
# ── DOCUMENT GENERATION ───────────────────────────────────────────────────
|
| 206 |
+
DOC_PLAIN = (
|
| 207 |
+
"\n\nCRITICAL FORMATTING RULES: "
|
| 208 |
+
"Write in plain, clear, forceful English prose only. "
|
| 209 |
+
"No JSON. No code. No markdown symbols, asterisks, or hash symbols. "
|
| 210 |
+
"No brackets like {{ or }}. Use numbered paragraphs and clear headings in CAPITALS. "
|
| 211 |
+
"This is a formal legal document. Every sentence must carry weight. "
|
| 212 |
+
"Do not hedge. Do not say 'may' or 'allegedly' or 'it appears'. "
|
| 213 |
+
"Breaches have occurred. Name them. Prove them. Demand remedy."
|
| 214 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 215 |
|
| 216 |
+
def generate_letter_before_action(r1, r2, r3, r4):
|
| 217 |
+
"""Letter Before Action — formal Pre-Action Protocol letter demanding remedy or litigation follows."""
|
| 218 |
+
if not r1:
|
| 219 |
+
return "Run the analysis first."
|
| 220 |
+
prompt = (
|
| 221 |
+
"You are a senior litigation solicitor drafting a Letter Before Action on behalf of "
|
| 222 |
+
"Dwayne Anthony Brian Galloway (the Claimant) against the named respondents identified in the analysis below.\n\n"
|
| 223 |
+
"ANALYSIS:\n"
|
| 224 |
+
f"FACT LEDGER:\n{r1[:2000]}\n\nBREACH ANALYSIS:\n{r2[:2000]}\n\nCASE SUMMARY:\n{r4[:2000]}\n\n"
|
| 225 |
+
"DRAFT A FORMAL LETTER BEFORE ACTION that:\n\n"
|
| 226 |
+
"1. Opens by stating that this letter is sent under the Pre-Action Protocol and that proceedings will be issued in the absence of a satisfactory response within 14 days.\n"
|
| 227 |
+
"2. States the full name of each respondent and each confirmed breach against them — by statute, section, and date. Do not allege. State.\n"
|
| 228 |
+
"3. States every loss, harm and injury caused to the Claimant as a direct result of each breach, including financial loss, physical harm, psychological harm, and any aggravating circumstances relating to the Claimant's disability.\n"
|
| 229 |
+
"4. States the specific legal remedies demanded: compensation, reasonable adjustments, correction of records, regulatory referral, costs — itemised.\n"
|
| 230 |
+
"5. States clearly: if full remedy is not provided within 14 days, proceedings will be issued in the King's Bench Division without further notice, and the Claimant will seek indemnity costs on the basis of the respondents' conduct.\n"
|
| 231 |
+
"6. Closes with the Claimant's name: Dwayne Anthony Brian Galloway, Litigant in Person.\n\n"
|
| 232 |
+
"This letter must have a spine. It is not a complaint. It is a final warning before litigation."
|
| 233 |
+
+ DOC_PLAIN
|
| 234 |
+
)
|
| 235 |
+
return call_llama(prompt, max_tokens=2000)
|
| 236 |
|
|
|
|
|
|
|
| 237 |
|
| 238 |
+
def generate_formal_complaint(r1, r2, r3, r4):
|
| 239 |
+
"""Formal Complaint — for submission to FOS, FCA, ICO, Parliamentary Ombudsman or regulator."""
|
| 240 |
+
if not r1:
|
| 241 |
+
return "Run the analysis first."
|
| 242 |
+
prompt = (
|
| 243 |
+
"You are drafting a formal regulatory complaint on behalf of Dwayne Anthony Brian Galloway "
|
| 244 |
+
"for submission to the relevant regulatory body — Financial Ombudsman Service, FCA, ICO, or Parliamentary and Health Service Ombudsman as appropriate based on the breaches identified.\n\n"
|
| 245 |
+
"ANALYSIS:\n"
|
| 246 |
+
f"FACT LEDGER:\n{r1[:2000]}\n\nBREACH ANALYSIS:\n{r2[:2000]}\n\nLOOP ANALYSIS:\n{r3[:1500]}\n\n"
|
| 247 |
+
"DRAFT A FORMAL REGULATORY COMPLAINT that:\n\n"
|
| 248 |
+
"1. Names the respondent institution and the regulatory framework they are bound by.\n"
|
| 249 |
+
"2. States the date the duty arose and the date it was breached — each breach as a numbered finding.\n"
|
| 250 |
+
"3. States every statutory provision breached with full citation: Act, section, and subsection.\n"
|
| 251 |
+
"4. States the harm caused by each breach in concrete terms: financial loss quantified, disability discrimination confirmed under Equality Act 2010 s.13 and s.20, hardship documented.\n"
|
| 252 |
+
"5. States what the respondent was required to do, what they did instead, and what that proves about their conduct.\n"
|
| 253 |
+
"6. States what remedy the Claimant requires from the regulator: investigation, enforcement, compensation, referral to FCA, referral to SRA, or public censure.\n"
|
| 254 |
+
"7. States that the Claimant is a disabled person as defined under Equality Act 2010 s.6 and that the respondent's conduct throughout has been discriminatory in nature, intent, and effect.\n"
|
| 255 |
+
"8. Closes with a demand that the regulator open a formal investigation and provide a substantive response within 8 weeks.\n\n"
|
| 256 |
+
"This complaint must be comprehensive, numbered, and impossible to dismiss as informal."
|
| 257 |
+
+ DOC_PLAIN
|
| 258 |
+
)
|
| 259 |
+
return call_llama(prompt, max_tokens=2000)
|
| 260 |
|
|
|
|
|
|
|
| 261 |
|
| 262 |
+
def generate_witness_statement(r1, r2, r4):
|
| 263 |
+
"""Witness Statement — first person, court-ready, signed with a statement of truth."""
|
| 264 |
+
if not r1:
|
| 265 |
+
return "Run the analysis first."
|
| 266 |
+
prompt = (
|
| 267 |
+
"You are drafting a formal Witness Statement for Dwayne Anthony Brian Galloway to file in civil proceedings.\n\n"
|
| 268 |
+
"ANALYSIS:\n"
|
| 269 |
+
f"FACT LEDGER:\n{r1[:2000]}\n\nBREACH ANALYSIS:\n{r2[:1500]}\n\nCASE SUMMARY:\n{r4[:1500]}\n\n"
|
| 270 |
+
"DRAFT A FORMAL WITNESS STATEMENT in the first person (I, Dwayne Anthony Brian Galloway, state as follows:) that:\n\n"
|
| 271 |
+
"1. Opens with the formal heading: IN THE HIGH COURT OF JUSTICE, KING'S BENCH DIVISION. Claim No. [TBC]. WITNESS STATEMENT OF DWAYNE ANTHONY BRIAN GALLOWAY.\n"
|
| 272 |
+
"2. States the Claimant's identity, disability status under Equality Act 2010 s.6, and the basis of the claim.\n"
|
| 273 |
+
"3. Narrates the full chronology of events in numbered paragraphs — what happened, when, who did it, and what was said or written.\n"
|
| 274 |
+
"4. For each breach: states what the Claimant was told, what the Claimant did in response, and how the Claimant was harmed.\n"
|
| 275 |
+
"5. States the financial losses, hardship, physical harm and psychological impact in the Claimant's own words.\n"
|
| 276 |
+
"6. Addresses every denial by the respondents and states why each denial is contradicted by the documented record.\n"
|
| 277 |
+
"7. States what the Claimant is asking the Court to find and to order.\n"
|
| 278 |
+
"8. Closes with the formal Statement of Truth: I believe that the facts stated in this witness statement are true. I understand that proceedings for contempt of court may be brought against anyone who makes, or causes to be made, a false statement in a document verified by a statement of truth. Signed: Dwayne Anthony Brian Galloway. Date: [DATE].\n\n"
|
| 279 |
+
"This statement must read as the account of a composed, credible, determined Claimant who knows exactly what was done to him and why it was unlawful."
|
| 280 |
+
+ DOC_PLAIN
|
| 281 |
+
)
|
| 282 |
+
return call_llama(prompt, max_tokens=2000)
|
| 283 |
|
|
|
|
|
|
|
|
|
|
| 284 |
|
| 285 |
+
def generate_skeleton_argument(r1, r2, r3, r4):
|
| 286 |
+
"""Court Skeleton Argument — structured legal argument for hearing."""
|
| 287 |
+
if not r1:
|
| 288 |
+
return "Run the analysis first."
|
| 289 |
+
prompt = (
|
| 290 |
+
"You are a barrister drafting a Skeleton Argument for Dwayne Anthony Brian Galloway, "
|
| 291 |
+
"Litigant in Person, for hearing in the King's Bench Division of the High Court.\n\n"
|
| 292 |
+
"ANALYSIS:\n"
|
| 293 |
+
f"FACT LEDGER:\n{r1[:1500]}\n\nBREACH ANALYSIS:\n{r2[:1500]}\n\nLOOP ANALYSIS:\n{r3[:1000]}\n\nCASE SUMMARY:\n{r4[:1500]}\n\n"
|
| 294 |
+
"DRAFT A SKELETON ARGUMENT that:\n\n"
|
| 295 |
+
"1. Opens with the case heading: IN THE HIGH COURT OF JUSTICE, KING'S BENCH DIVISION. Between: DWAYNE ANTHONY BRIAN GALLOWAY (Claimant) and [RESPONDENTS] (Defendants). SKELETON ARGUMENT OF THE CLAIMANT.\n"
|
| 296 |
+
"2. States the issues for determination — numbered, clear, each one a question the Court must answer.\n"
|
| 297 |
+
"3. Sets out the Legal Framework: every statute and case law authority relied upon, cited correctly (Act, section; case name [year] court, report).\n"
|
| 298 |
+
"4. States the Claimant's submissions on each issue: what the law requires, what the Defendant did, why that is a breach, and what the Court must find.\n"
|
| 299 |
+
"5. Addresses the Defendant's position and explains precisely why each element of it is wrong in law and on the facts.\n"
|
| 300 |
+
"6. States the relief sought: damages quantified, declarations sought, costs on the indemnity basis, and any injunctive relief.\n"
|
| 301 |
+
"7. Closes with a summary of why the Claimant must succeed and why the Defendants have no sustainable defence.\n\n"
|
| 302 |
+
"Authorities to consider citing where the facts support them:\n"
|
| 303 |
+
"Archibald v Fife Council [2004] UKHL 32 (reasonable adjustments — disability).\n"
|
| 304 |
+
"Associated Provincial Picture Houses Ltd v Wednesbury Corporation [1948] 1 KB 223 (unreasonableness).\n"
|
| 305 |
+
"Consumer Rights Act 2015 ss.9, 10, 11, 14, 19.\n"
|
| 306 |
+
"Equality Act 2010 ss.6, 13, 20, 21, 26, 29, 110.\n"
|
| 307 |
+
"Fraud Act 2006 s.2 (fraud by false representation).\n"
|
| 308 |
+
"Consumer Protection from Unfair Trading Regulations 2008.\n"
|
| 309 |
+
"Financial Services and Markets Act 2000 and FCA CONC/BCOBS sourcebooks.\n\n"
|
| 310 |
+
"This skeleton must be tight, authoritative, and leave the Defendants nowhere to stand."
|
| 311 |
+
+ DOC_PLAIN
|
| 312 |
+
)
|
| 313 |
+
return call_llama(prompt, max_tokens=2000)
|
| 314 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 315 |
|
| 316 |
+
def generate_regulatory_referral(r1, r2, r3):
|
| 317 |
+
"""Regulatory Referral — letter to FCA, SRA, ICO, or Parliamentary Ombudsman."""
|
| 318 |
+
if not r1:
|
| 319 |
+
return "Run the analysis first."
|
| 320 |
+
prompt = (
|
| 321 |
+
"You are drafting a formal referral letter to a regulatory authority on behalf of "
|
| 322 |
+
"Dwayne Anthony Brian Galloway.\n\n"
|
| 323 |
+
"ANALYSIS:\n"
|
| 324 |
+
f"FACT LEDGER:\n{r1[:1500]}\n\nBREACH ANALYSIS:\n{r2[:1500]}\n\nLOOP ANALYSIS:\n{r3[:1500]}\n\n"
|
| 325 |
+
"DRAFT A REGULATORY REFERRAL LETTER addressed to the appropriate regulator (FCA, SRA, ICO, or Ombudsman as the breaches dictate) that:\n\n"
|
| 326 |
+
"1. States at the outset that this is a formal referral under the relevant statutory or regulatory framework and that the Claimant requires the regulator to open a formal investigation.\n"
|
| 327 |
+
"2. Identifies each regulated entity by name and their regulatory authorisation number or registration.\n"
|
| 328 |
+
"3. States each confirmed breach as a numbered finding: the conduct, the statutory provision breached, the date, and the harm caused.\n"
|
| 329 |
+
"4. States that the conduct amounts to: regulatory misconduct, failure of regulated duty, discriminatory treatment of a disabled customer under Equality Act 2010, and where applicable, conduct unbecoming of a regulated legal professional under the SRA Code of Conduct 2019.\n"
|
| 330 |
+
"5. States the specific regulatory action the Claimant requires: investigation, enforcement notice, fine, suspension, public censure, or referral to another authority.\n"
|
| 331 |
+
"6. States that the Claimant is a disabled person under EA 2010 s.6 and that failure by the regulator to investigate constitutes a further failure to uphold equality obligations.\n"
|
| 332 |
+
"7. Closes with a 28-day deadline for a substantive response and confirmation that in the absence of action, the Claimant will escalate to the Parliamentary and Health Service Ombudsman and the national press.\n\n"
|
| 333 |
+
"This letter must leave the regulator in no doubt that inaction will have consequences."
|
| 334 |
+
+ DOC_PLAIN
|
| 335 |
+
)
|
| 336 |
+
return call_llama(prompt, max_tokens=2000)
|
| 337 |
|
|
|
|
|
|
|
| 338 |
|
| 339 |
+
def generate_all_documents(r1, r2, r3, r4):
|
| 340 |
+
"""Generate all five documents from the analysis outputs."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 341 |
if not r1:
|
| 342 |
+
return "", "", "", "", "⚠️ Run the analysis first, then generate documents."
|
| 343 |
+
lba = generate_letter_before_action(r1, r2, r3, r4)
|
| 344 |
+
comp = generate_formal_complaint(r1, r2, r3, r4)
|
| 345 |
+
wit = generate_witness_statement(r1, r2, r4)
|
| 346 |
+
skel = generate_skeleton_argument(r1, r2, r3, r4)
|
| 347 |
+
reg = generate_regulatory_referral(r1, r2, r3)
|
| 348 |
+
return lba, comp, wit, skel, reg
|
| 349 |
+
|
| 350 |
+
|
| 351 |
+
def save_documents(lba, comp, wit, skel, reg):
|
| 352 |
+
"""Save all generated documents to a single download file."""
|
| 353 |
+
if not any([lba, comp, wit, skel, reg]):
|
| 354 |
+
return None, "⚠️ Generate documents first."
|
| 355 |
ts = time.strftime("%Y-%m-%d %H:%M")
|
| 356 |
content = (
|
| 357 |
+
f"METABOLIC PHOENIX FRAMEWORK — LEGAL DOCUMENTS\n"
|
| 358 |
f"Generated: {ts}\n"
|
| 359 |
+
f"Dwayne Anthony Brian Galloway | Injectiv8\n\n"
|
| 360 |
+
f"{'='*60}\n\nDOCUMENT 1 — LETTER BEFORE ACTION\n\n{lba}\n\n"
|
| 361 |
+
f"{'='*60}\n\nDOCUMENT 2 — FORMAL REGULATORY COMPLAINT\n\n{comp}\n\n"
|
| 362 |
+
f"{'='*60}\n\nDOCUMENT 3 — WITNESS STATEMENT\n\n{wit}\n\n"
|
| 363 |
+
f"{'='*60}\n\nDOCUMENT 4 — SKELETON ARGUMENT\n\n{skel}\n\n"
|
| 364 |
+
f"{'='*60}\n\nDOCUMENT 5 — REGULATORY REFERRAL\n\n{reg}"
|
| 365 |
)
|
| 366 |
tmp = tempfile.NamedTemporaryFile(
|
| 367 |
delete=False, suffix=".txt",
|
| 368 |
+
prefix=f"MPF_Documents_{time.strftime('%Y%m%d_%H%M')}_",
|
| 369 |
mode="w", encoding="utf-8"
|
| 370 |
)
|
| 371 |
tmp.write(content)
|
| 372 |
tmp.close()
|
| 373 |
+
return tmp.name, "✅ All documents ready to download."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 374 |
|
| 375 |
|
| 376 |
# ── FULL TTS — NO 30-SECOND CAP ──────────────────────────────────────────
|
|
|
|
| 652 |
dl_file = gr.File(label="Download your analysis")
|
| 653 |
save_msg = gr.Markdown("")
|
| 654 |
|
| 655 |
+
# ── DOCUMENT GENERATION ──────────────────────────────────────────────
|
| 656 |
+
gr.HTML("<hr style='border-color:#1a2544;margin:2rem 0;'>")
|
| 657 |
+
gr.HTML("""
|
| 658 |
+
<div style='text-align:center;padding:1.2rem 0 0.6rem;'>
|
| 659 |
+
<h2 style='font-size:1.5rem;font-weight:800;letter-spacing:-0.03em;
|
| 660 |
+
background:linear-gradient(135deg,#FFE500,#FF6B00);
|
| 661 |
+
-webkit-background-clip:text;-webkit-text-fill-color:transparent;margin:0 0 0.3rem;'>
|
| 662 |
+
⚖️ Document Generation Engine
|
| 663 |
+
</h2>
|
| 664 |
+
<p style='color:#4A6080;font-family:"DM Mono",monospace;font-size:.73rem;letter-spacing:.05em;'>
|
| 665 |
+
RUN ANALYSIS FIRST — THEN GENERATE DOCUMENTS WITH SPINE
|
| 666 |
+
</p>
|
| 667 |
+
</div>
|
| 668 |
+
""")
|
| 669 |
+
|
| 670 |
+
gen_all_btn = gr.Button("⚖️ Generate ALL Documents", variant="primary")
|
| 671 |
+
|
| 672 |
+
with gr.Accordion("Document 1 — Letter Before Action", open=True):
|
| 673 |
+
gr.HTML("<p style='color:#4A6080;font-size:.75rem;font-family:DM Mono,monospace;padding:.3rem 0;'>Pre-Action Protocol letter. 14-day deadline. Litigation follows without further notice.</p>")
|
| 674 |
+
gen_lba_btn = gr.Button("Generate Letter Before Action", variant="secondary")
|
| 675 |
+
doc_lba = gr.Textbox(label="", lines=14, elem_classes=["out"])
|
| 676 |
+
rd_lba_btn = gr.Button("🔊 Read Aloud", variant="secondary")
|
| 677 |
+
aud_lba = gr.Audio(label="LBA Audio", autoplay=True)
|
| 678 |
+
|
| 679 |
+
with gr.Accordion("Document 2 — Formal Regulatory Complaint", open=False):
|
| 680 |
+
gr.HTML("<p style='color:#4A6080;font-size:.75rem;font-family:DM Mono,monospace;padding:.3rem 0;'>For FOS, FCA, ICO, Parliamentary Ombudsman. Numbered findings. Demands investigation.</p>")
|
| 681 |
+
gen_comp_btn = gr.Button("Generate Formal Complaint", variant="secondary")
|
| 682 |
+
doc_comp = gr.Textbox(label="", lines=14, elem_classes=["out"])
|
| 683 |
+
rd_comp_btn = gr.Button("🔊 Read Aloud", variant="secondary")
|
| 684 |
+
aud_comp = gr.Audio(label="Complaint Audio", autoplay=True)
|
| 685 |
+
|
| 686 |
+
with gr.Accordion("Document 3 — Witness Statement", open=False):
|
| 687 |
+
gr.HTML("<p style='color:#4A6080;font-size:.75rem;font-family:DM Mono,monospace;padding:.3rem 0;'>First person. Court-ready. Signed with Statement of Truth. King's Bench Division heading.</p>")
|
| 688 |
+
gen_wit_btn = gr.Button("Generate Witness Statement", variant="secondary")
|
| 689 |
+
doc_wit = gr.Textbox(label="", lines=14, elem_classes=["out"])
|
| 690 |
+
rd_wit_btn = gr.Button("🔊 Read Aloud", variant="secondary")
|
| 691 |
+
aud_wit = gr.Audio(label="Witness Statement Audio", autoplay=True)
|
| 692 |
+
|
| 693 |
+
with gr.Accordion("Document 4 — Skeleton Argument", open=False):
|
| 694 |
+
gr.HTML("<p style='color:#4A6080;font-size:.75rem;font-family:DM Mono,monospace;padding:.3rem 0;'>Structured legal argument. Authorities cited. Issues for determination. Relief sought.</p>")
|
| 695 |
+
gen_skel_btn = gr.Button("Generate Skeleton Argument", variant="secondary")
|
| 696 |
+
doc_skel = gr.Textbox(label="", lines=14, elem_classes=["out"])
|
| 697 |
+
rd_skel_btn = gr.Button("🔊 Read Aloud", variant="secondary")
|
| 698 |
+
aud_skel = gr.Audio(label="Skeleton Audio", autoplay=True)
|
| 699 |
+
|
| 700 |
+
with gr.Accordion("Document 5 — Regulatory Referral", open=False):
|
| 701 |
+
gr.HTML("<p style='color:#4A6080;font-size:.75rem;font-family:DM Mono,monospace;padding:.3rem 0;'>FCA / SRA / ICO referral. 28-day response deadline. Escalation to press if no action.</p>")
|
| 702 |
+
gen_reg_btn = gr.Button("Generate Regulatory Referral", variant="secondary")
|
| 703 |
+
doc_reg = gr.Textbox(label="", lines=14, elem_classes=["out"])
|
| 704 |
+
rd_reg_btn = gr.Button("🔊 Read Aloud", variant="secondary")
|
| 705 |
+
aud_reg = gr.Audio(label="Referral Audio", autoplay=True)
|
| 706 |
+
|
| 707 |
+
save_docs_btn = gr.Button("💾 Save & Download All Documents", variant="secondary")
|
| 708 |
+
dl_docs_file = gr.File(label="Download all documents")
|
| 709 |
+
save_docs_msg = gr.Markdown("")
|
| 710 |
+
|
| 711 |
run_btn.click(analyse, inputs=[pdf_input], outputs=[out1, out2, out3, out4])
|
| 712 |
read_doc_btn.click(read_document_only, inputs=[pdf_input, speed_slider, pitch_slider], outputs=[read_doc_audio, read_doc_msg])
|
| 713 |
rb1_btn.click(read_cycle1, inputs=[out1, speed_slider, pitch_slider], outputs=[audio1])
|
|
|
|
| 717 |
rball_btn.click(read_all, inputs=[out1, out2, out3, out4, speed_slider, pitch_slider], outputs=[audio_all])
|
| 718 |
save_btn.click(save_analysis, inputs=[out1, out2, out3, out4], outputs=[dl_file, save_msg])
|
| 719 |
|
| 720 |
+
# Document generation bindings
|
| 721 |
+
gen_all_btn.click(generate_all_documents, inputs=[out1, out2, out3, out4], outputs=[doc_lba, doc_comp, doc_wit, doc_skel, doc_reg])
|
| 722 |
+
gen_lba_btn.click(generate_letter_before_action, inputs=[out1, out2, out3, out4], outputs=[doc_lba])
|
| 723 |
+
gen_comp_btn.click(generate_formal_complaint, inputs=[out1, out2, out3, out4], outputs=[doc_comp])
|
| 724 |
+
gen_wit_btn.click(generate_witness_statement, inputs=[out1, out2, out4], outputs=[doc_wit])
|
| 725 |
+
gen_skel_btn.click(generate_skeleton_argument, inputs=[out1, out2, out3, out4], outputs=[doc_skel])
|
| 726 |
+
gen_reg_btn.click(generate_regulatory_referral, inputs=[out1, out2, out3], outputs=[doc_reg])
|
| 727 |
+
|
| 728 |
+
rd_lba_btn.click(make_audio, inputs=[doc_lba, speed_slider, pitch_slider], outputs=[aud_lba])
|
| 729 |
+
rd_comp_btn.click(make_audio, inputs=[doc_comp, speed_slider, pitch_slider], outputs=[aud_comp])
|
| 730 |
+
rd_wit_btn.click(make_audio, inputs=[doc_wit, speed_slider, pitch_slider], outputs=[aud_wit])
|
| 731 |
+
rd_skel_btn.click(make_audio, inputs=[doc_skel, speed_slider, pitch_slider], outputs=[aud_skel])
|
| 732 |
+
rd_reg_btn.click(make_audio, inputs=[doc_reg, speed_slider, pitch_slider], outputs=[aud_reg])
|
| 733 |
+
|
| 734 |
+
save_docs_btn.click(save_documents, inputs=[doc_lba, doc_comp, doc_wit, doc_skel, doc_reg], outputs=[dl_docs_file, save_docs_msg])
|
| 735 |
+
|
| 736 |
gr.HTML("""
|
| 737 |
<div class="foot">
|
| 738 |
THE METABOLIC PHOENIX FRAMEWORK · Injectiv8 · Ms. Nala
|
|
|
|
| 742 |
|
| 743 |
if __name__ == "__main__":
|
| 744 |
demo.launch()
|
|
|