Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -30,15 +30,15 @@ _MODELS = [
|
|
| 30 |
def call_llama(prompt: str, max_tokens: int = 2000) -> str:
|
| 31 |
token = os.environ.get("HF_TOKEN", "")
|
| 32 |
headers = {"Authorization": f"Bearer {token}"} if token else {}
|
|
|
|
| 33 |
for model in _MODELS:
|
| 34 |
-
for attempt in range(
|
| 35 |
try:
|
| 36 |
-
fmt = prompt
|
| 37 |
resp = _req.post(
|
| 38 |
_FREE_API.format(model=model),
|
| 39 |
headers=headers,
|
| 40 |
json={
|
| 41 |
-
"inputs":
|
| 42 |
"parameters": {
|
| 43 |
"max_new_tokens": max_tokens,
|
| 44 |
"temperature": 0.7,
|
|
@@ -47,8 +47,9 @@ def call_llama(prompt: str, max_tokens: int = 2000) -> str:
|
|
| 47 |
},
|
| 48 |
"options": {"wait_for_model": True}
|
| 49 |
},
|
| 50 |
-
timeout=
|
| 51 |
)
|
|
|
|
| 52 |
if resp.status_code == 200:
|
| 53 |
result = resp.json()
|
| 54 |
if isinstance(result, list) and result:
|
|
@@ -56,16 +57,17 @@ def call_llama(prompt: str, max_tokens: int = 2000) -> str:
|
|
| 56 |
if text:
|
| 57 |
return text
|
| 58 |
elif resp.status_code == 503:
|
| 59 |
-
time.sleep(
|
| 60 |
continue
|
| 61 |
elif resp.status_code in [429, 402]:
|
| 62 |
break
|
| 63 |
else:
|
| 64 |
-
time.sleep(
|
| 65 |
continue
|
| 66 |
-
except Exception:
|
| 67 |
-
time.sleep(
|
| 68 |
continue
|
|
|
|
| 69 |
return "Could not reach AI. Models may be loading — please try again in 30 seconds."
|
| 70 |
|
| 71 |
# =============================================================================
|
|
@@ -81,7 +83,7 @@ def extract_pdf_text(path):
|
|
| 81 |
pages.append(f"--- Page {i+1} ---\n{text.strip()}")
|
| 82 |
return "\n\n".join(pages) if pages else "[No extractable text — may be scanned PDF]"
|
| 83 |
except Exception as e:
|
| 84 |
-
return f"[PDF read error: {e}]"
|
| 85 |
|
| 86 |
def extract_image_text(path):
|
| 87 |
try:
|
|
@@ -89,7 +91,7 @@ def extract_image_text(path):
|
|
| 89 |
text = pytesseract.image_to_string(img, lang="eng")
|
| 90 |
return f"[IMAGE OCR]\n{text.strip()}" if text.strip() else "[Image: no text detected]"
|
| 91 |
except Exception as e:
|
| 92 |
-
return f"[Image OCR error: {e}]"
|
| 93 |
|
| 94 |
def transcribe_audio(path):
|
| 95 |
try:
|
|
@@ -97,13 +99,14 @@ def transcribe_audio(path):
|
|
| 97 |
result = model.transcribe(path, language="en")
|
| 98 |
return f"[AUDIO TRANSCRIPTION]\n{result['text'].strip()}"
|
| 99 |
except Exception as e:
|
| 100 |
-
return f"[Transcription error: {e}]"
|
| 101 |
|
| 102 |
def extract_content(file):
|
| 103 |
if not file:
|
| 104 |
return ""
|
| 105 |
path = file.name if hasattr(file, "name") else str(file)
|
| 106 |
ext = Path(path).suffix.lower()
|
|
|
|
| 107 |
if ext == ".pdf":
|
| 108 |
return extract_pdf_text(path)
|
| 109 |
elif ext in [".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".tiff"]:
|
|
@@ -117,9 +120,12 @@ def extract_content(file):
|
|
| 117 |
capture_output=True, timeout=120)
|
| 118 |
return transcribe_audio(tmp.name)
|
| 119 |
except Exception as e:
|
| 120 |
-
return f"[Video extraction error: {e}]"
|
| 121 |
elif ext in [".txt", ".md"]:
|
| 122 |
-
|
|
|
|
|
|
|
|
|
|
| 123 |
else:
|
| 124 |
return f"[Unsupported file type: {ext}]"
|
| 125 |
|
|
@@ -196,9 +202,8 @@ def analyse(files):
|
|
| 196 |
r4 = call_llama(
|
| 197 |
f"You are Layoutbot — part of the Metabolic Phoenix Framework "
|
| 198 |
f"by Dwayne Anthony Brian Galloway.\n\n"
|
| 199 |
-
f"
|
| 200 |
-
f"
|
| 201 |
-
f"LOOP OUTPUT:\n{r3[:1500]}\n\n"
|
| 202 |
f"{'='*60}\n\n"
|
| 203 |
"Produce a complete litigation-ready case summary. "
|
| 204 |
"Breaches have occurred and are on the record. Write accordingly.\n\n"
|
|
@@ -207,13 +212,27 @@ def analyse(files):
|
|
| 207 |
"TOP 5 ARGUMENTS — strongest first, each grounded in confirmed fact and statute\n"
|
| 208 |
"REQUIRED NEXT STEPS — mandatory actions in priority order\n"
|
| 209 |
"ESCALATION MAP — which body receives what, by what deadline\n\n"
|
| 210 |
-
"Ms. Nala | Injectiv8 | Dwayne Anthony Brian Galloway" + PLAIN_ENGLISH
|
|
|
|
| 211 |
)
|
| 212 |
return r1, r2, r3, r4
|
| 213 |
|
| 214 |
# =============================================================================
|
| 215 |
# TTS (edge-tts + pydub)
|
| 216 |
# =============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 217 |
async def _edge_speak(text, path, voice):
|
| 218 |
await edge_tts.Communicate(text, voice).save(path)
|
| 219 |
|
|
@@ -265,11 +284,11 @@ def text_to_speech_full(text, speed=1.0, pitch=1.0):
|
|
| 265 |
return None
|
| 266 |
chunks = _split_chunks(text)
|
| 267 |
voices = ["en-GB-SoniaNeural", "en-GB-RyanNeural"]
|
|
|
|
| 268 |
for voice in voices:
|
| 269 |
files = []
|
| 270 |
try:
|
| 271 |
-
loop =
|
| 272 |
-
asyncio.set_event_loop(loop)
|
| 273 |
for i, chunk in enumerate(chunks):
|
| 274 |
out = tempfile.NamedTemporaryFile(delete=False, suffix=f"_{i}.mp3", dir="/tmp")
|
| 275 |
out.close()
|
|
@@ -282,13 +301,14 @@ def text_to_speech_full(text, speed=1.0, pitch=1.0):
|
|
| 282 |
except Exception:
|
| 283 |
try: os.unlink(out.name)
|
| 284 |
except Exception: pass
|
| 285 |
-
|
| 286 |
if len(files) >= len(chunks) * 0.8:
|
| 287 |
result = _build_audio(files, speed, pitch)
|
| 288 |
if result:
|
| 289 |
return result
|
| 290 |
except Exception:
|
| 291 |
pass
|
|
|
|
| 292 |
# fallback to gTTS
|
| 293 |
try:
|
| 294 |
from gtts import gTTS
|
|
@@ -313,17 +333,17 @@ def make_audio(text, speed=1.0, pitch=1.0):
|
|
| 313 |
return text_to_speech_full(text, speed, pitch)
|
| 314 |
|
| 315 |
# =============================================================================
|
| 316 |
-
# DOCUMENT GENERATION
|
| 317 |
# =============================================================================
|
| 318 |
def generate_dummy(r1, r2, r3, r4, typ: str):
|
| 319 |
if not r1:
|
| 320 |
return "Run analysis first."
|
| 321 |
prompt_map = {
|
| 322 |
-
"letter": "Draft a formal Letter Before Action.
|
| 323 |
-
"complaint": "Draft a formal regulatory complaint.
|
| 324 |
-
"witness": "Draft a Witness Statement.
|
| 325 |
-
"skeleton": "Draft a Skeleton Argument.
|
| 326 |
-
"referral": "Draft a Regulatory Referral.
|
| 327 |
}
|
| 328 |
prompt = prompt_map.get(typ, "")
|
| 329 |
return call_llama(
|
|
@@ -396,6 +416,9 @@ with gr.Blocks(css=CSS, title="Metabolic Phoenix Framework") as demo:
|
|
| 396 |
<p class="own">Invented & Owned by Dwayne Anthony Brian Galloway · 2026</p>
|
| 397 |
</div>""")
|
| 398 |
|
|
|
|
|
|
|
|
|
|
| 399 |
pdf_input = gr.File(
|
| 400 |
label="Upload Documents — PDF · Image · Audio · Video · Text — drop multiple",
|
| 401 |
file_types=[
|
|
@@ -433,15 +456,15 @@ with gr.Blocks(css=CSS, title="Metabolic Phoenix Framework") as demo:
|
|
| 433 |
gen_referral = gr.Button("Regulatory Referral", variant="secondary")
|
| 434 |
doc_output = gr.Textbox(label="Generated Document", lines=20)
|
| 435 |
|
| 436 |
-
|
| 437 |
-
|
| 438 |
-
return r1, r2, r3, r4
|
| 439 |
|
| 440 |
-
# Analyse
|
| 441 |
analyse_btn.click(
|
| 442 |
-
fn=
|
| 443 |
inputs=pdf_input,
|
| 444 |
-
outputs=[out1, out2, out3, out4]
|
| 445 |
)
|
| 446 |
|
| 447 |
# TTS for each cycle
|
|
@@ -452,20 +475,15 @@ with gr.Blocks(css=CSS, title="Metabolic Phoenix Framework") as demo:
|
|
| 452 |
|
| 453 |
# Document reading without analysis
|
| 454 |
read_doc_btn.click(
|
| 455 |
-
fn=lambda files
|
| 456 |
-
"\n\n".join([extract_content(f) for f in (files if isinstance(files, list) else [files])]),
|
| 457 |
-
|
| 458 |
),
|
| 459 |
-
inputs=
|
| 460 |
-
gr.Slider(0.5, 2.0, value=1.0, label="Pitch")],
|
| 461 |
outputs=read_doc_audio
|
| 462 |
)
|
| 463 |
|
| 464 |
-
# Document generation (
|
| 465 |
-
analysis_state = gr.State([None, None, None, None])
|
| 466 |
-
for out in [out1, out2, out3, out4]:
|
| 467 |
-
out.change(lambda *vals: vals, [out1, out2, out3, out4], analysis_state)
|
| 468 |
-
|
| 469 |
gen_letter.click(fn=lambda s: generate_letter(*s), inputs=analysis_state, outputs=doc_output)
|
| 470 |
gen_complaint.click(fn=lambda s: generate_complaint(*s), inputs=analysis_state, outputs=doc_output)
|
| 471 |
gen_witness.click(fn=lambda s: generate_witness(*s), inputs=analysis_state, outputs=doc_output)
|
|
@@ -477,4 +495,4 @@ with gr.Blocks(css=CSS, title="Metabolic Phoenix Framework") as demo:
|
|
| 477 |
</div>""")
|
| 478 |
|
| 479 |
if __name__ == "__main__":
|
| 480 |
-
demo.launch()
|
|
|
|
| 30 |
def call_llama(prompt: str, max_tokens: int = 2000) -> str:
|
| 31 |
token = os.environ.get("HF_TOKEN", "")
|
| 32 |
headers = {"Authorization": f"Bearer {token}"} if token else {}
|
| 33 |
+
|
| 34 |
for model in _MODELS:
|
| 35 |
+
for attempt in range(3):
|
| 36 |
try:
|
|
|
|
| 37 |
resp = _req.post(
|
| 38 |
_FREE_API.format(model=model),
|
| 39 |
headers=headers,
|
| 40 |
json={
|
| 41 |
+
"inputs": prompt,
|
| 42 |
"parameters": {
|
| 43 |
"max_new_tokens": max_tokens,
|
| 44 |
"temperature": 0.7,
|
|
|
|
| 47 |
},
|
| 48 |
"options": {"wait_for_model": True}
|
| 49 |
},
|
| 50 |
+
timeout=60
|
| 51 |
)
|
| 52 |
+
|
| 53 |
if resp.status_code == 200:
|
| 54 |
result = resp.json()
|
| 55 |
if isinstance(result, list) and result:
|
|
|
|
| 57 |
if text:
|
| 58 |
return text
|
| 59 |
elif resp.status_code == 503:
|
| 60 |
+
time.sleep(15 * (attempt + 1))
|
| 61 |
continue
|
| 62 |
elif resp.status_code in [429, 402]:
|
| 63 |
break
|
| 64 |
else:
|
| 65 |
+
time.sleep(5)
|
| 66 |
continue
|
| 67 |
+
except Exception as e:
|
| 68 |
+
time.sleep(5)
|
| 69 |
continue
|
| 70 |
+
|
| 71 |
return "Could not reach AI. Models may be loading — please try again in 30 seconds."
|
| 72 |
|
| 73 |
# =============================================================================
|
|
|
|
| 83 |
pages.append(f"--- Page {i+1} ---\n{text.strip()}")
|
| 84 |
return "\n\n".join(pages) if pages else "[No extractable text — may be scanned PDF]"
|
| 85 |
except Exception as e:
|
| 86 |
+
return f"[PDF read error: {str(e)[:100]}]"
|
| 87 |
|
| 88 |
def extract_image_text(path):
|
| 89 |
try:
|
|
|
|
| 91 |
text = pytesseract.image_to_string(img, lang="eng")
|
| 92 |
return f"[IMAGE OCR]\n{text.strip()}" if text.strip() else "[Image: no text detected]"
|
| 93 |
except Exception as e:
|
| 94 |
+
return f"[Image OCR error: {str(e)[:100]}]"
|
| 95 |
|
| 96 |
def transcribe_audio(path):
|
| 97 |
try:
|
|
|
|
| 99 |
result = model.transcribe(path, language="en")
|
| 100 |
return f"[AUDIO TRANSCRIPTION]\n{result['text'].strip()}"
|
| 101 |
except Exception as e:
|
| 102 |
+
return f"[Transcription error: {str(e)[:100]}]"
|
| 103 |
|
| 104 |
def extract_content(file):
|
| 105 |
if not file:
|
| 106 |
return ""
|
| 107 |
path = file.name if hasattr(file, "name") else str(file)
|
| 108 |
ext = Path(path).suffix.lower()
|
| 109 |
+
|
| 110 |
if ext == ".pdf":
|
| 111 |
return extract_pdf_text(path)
|
| 112 |
elif ext in [".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".tiff"]:
|
|
|
|
| 120 |
capture_output=True, timeout=120)
|
| 121 |
return transcribe_audio(tmp.name)
|
| 122 |
except Exception as e:
|
| 123 |
+
return f"[Video extraction error: {str(e)[:100]}]"
|
| 124 |
elif ext in [".txt", ".md"]:
|
| 125 |
+
try:
|
| 126 |
+
return Path(path).read_text(encoding="utf-8", errors="replace")
|
| 127 |
+
except Exception as e:
|
| 128 |
+
return f"[Text read error: {str(e)[:100]}]"
|
| 129 |
else:
|
| 130 |
return f"[Unsupported file type: {ext}]"
|
| 131 |
|
|
|
|
| 202 |
r4 = call_llama(
|
| 203 |
f"You are Layoutbot — part of the Metabolic Phoenix Framework "
|
| 204 |
f"by Dwayne Anthony Brian Galloway.\n\n"
|
| 205 |
+
f"Fact Ledger:\n{r1[:1500]}\n\nBreach Analysis:\n{r2[:1500]}\n\n"
|
| 206 |
+
f"Loop Output:\n{r3[:1000]}\n\nCase Summary Context Complete\n\n"
|
|
|
|
| 207 |
f"{'='*60}\n\n"
|
| 208 |
"Produce a complete litigation-ready case summary. "
|
| 209 |
"Breaches have occurred and are on the record. Write accordingly.\n\n"
|
|
|
|
| 212 |
"TOP 5 ARGUMENTS — strongest first, each grounded in confirmed fact and statute\n"
|
| 213 |
"REQUIRED NEXT STEPS — mandatory actions in priority order\n"
|
| 214 |
"ESCALATION MAP — which body receives what, by what deadline\n\n"
|
| 215 |
+
"Ms. Nala | Injectiv8 | Dwayne Anthony Brian Galloway" + PLAIN_ENGLISH,
|
| 216 |
+
max_tokens=2500
|
| 217 |
)
|
| 218 |
return r1, r2, r3, r4
|
| 219 |
|
| 220 |
# =============================================================================
|
| 221 |
# TTS (edge-tts + pydub)
|
| 222 |
# =============================================================================
|
| 223 |
+
_event_loop = None
|
| 224 |
+
|
| 225 |
+
def _get_event_loop():
|
| 226 |
+
global _event_loop
|
| 227 |
+
try:
|
| 228 |
+
loop = asyncio.get_running_loop()
|
| 229 |
+
return loop
|
| 230 |
+
except RuntimeError:
|
| 231 |
+
if _event_loop is None or _event_loop.is_closed():
|
| 232 |
+
_event_loop = asyncio.new_event_loop()
|
| 233 |
+
asyncio.set_event_loop(_event_loop)
|
| 234 |
+
return _event_loop
|
| 235 |
+
|
| 236 |
async def _edge_speak(text, path, voice):
|
| 237 |
await edge_tts.Communicate(text, voice).save(path)
|
| 238 |
|
|
|
|
| 284 |
return None
|
| 285 |
chunks = _split_chunks(text)
|
| 286 |
voices = ["en-GB-SoniaNeural", "en-GB-RyanNeural"]
|
| 287 |
+
|
| 288 |
for voice in voices:
|
| 289 |
files = []
|
| 290 |
try:
|
| 291 |
+
loop = _get_event_loop()
|
|
|
|
| 292 |
for i, chunk in enumerate(chunks):
|
| 293 |
out = tempfile.NamedTemporaryFile(delete=False, suffix=f"_{i}.mp3", dir="/tmp")
|
| 294 |
out.close()
|
|
|
|
| 301 |
except Exception:
|
| 302 |
try: os.unlink(out.name)
|
| 303 |
except Exception: pass
|
| 304 |
+
|
| 305 |
if len(files) >= len(chunks) * 0.8:
|
| 306 |
result = _build_audio(files, speed, pitch)
|
| 307 |
if result:
|
| 308 |
return result
|
| 309 |
except Exception:
|
| 310 |
pass
|
| 311 |
+
|
| 312 |
# fallback to gTTS
|
| 313 |
try:
|
| 314 |
from gtts import gTTS
|
|
|
|
| 333 |
return text_to_speech_full(text, speed, pitch)
|
| 334 |
|
| 335 |
# =============================================================================
|
| 336 |
+
# DOCUMENT GENERATION
|
| 337 |
# =============================================================================
|
| 338 |
def generate_dummy(r1, r2, r3, r4, typ: str):
|
| 339 |
if not r1:
|
| 340 |
return "Run analysis first."
|
| 341 |
prompt_map = {
|
| 342 |
+
"letter": "Draft a formal Letter Before Action to the relevant institution naming the breach, citing statute, and demanding remedy within 14 days.",
|
| 343 |
+
"complaint": "Draft a formal regulatory complaint to the appropriate ombudsman or regulator, stating facts, breaches, and requested remedy.",
|
| 344 |
+
"witness": "Draft a Witness Statement in first person, confirming facts, dates, and personal knowledge of the breaches.",
|
| 345 |
+
"skeleton": "Draft a Skeleton Argument for court proceedings: issues in dispute, legal principles, case law, and conclusions.",
|
| 346 |
+
"referral": "Draft a Regulatory Referral setting out the systemic breaches and requesting investigation and enforcement action."
|
| 347 |
}
|
| 348 |
prompt = prompt_map.get(typ, "")
|
| 349 |
return call_llama(
|
|
|
|
| 416 |
<p class="own">Invented & Owned by Dwayne Anthony Brian Galloway · 2026</p>
|
| 417 |
</div>""")
|
| 418 |
|
| 419 |
+
# Analysis state to persist across cycles
|
| 420 |
+
analysis_state = gr.State([None, None, None, None])
|
| 421 |
+
|
| 422 |
pdf_input = gr.File(
|
| 423 |
label="Upload Documents — PDF · Image · Audio · Video · Text — drop multiple",
|
| 424 |
file_types=[
|
|
|
|
| 456 |
gen_referral = gr.Button("Regulatory Referral", variant="secondary")
|
| 457 |
doc_output = gr.Textbox(label="Generated Document", lines=20)
|
| 458 |
|
| 459 |
+
def analyse_and_update_state(files):
|
| 460 |
+
r1, r2, r3, r4 = analyse(files)
|
| 461 |
+
return r1, r2, r3, r4, [r1, r2, r3, r4]
|
| 462 |
|
| 463 |
+
# Analyse button: run analysis and update state
|
| 464 |
analyse_btn.click(
|
| 465 |
+
fn=analyse_and_update_state,
|
| 466 |
inputs=pdf_input,
|
| 467 |
+
outputs=[out1, out2, out3, out4, analysis_state]
|
| 468 |
)
|
| 469 |
|
| 470 |
# TTS for each cycle
|
|
|
|
| 475 |
|
| 476 |
# Document reading without analysis
|
| 477 |
read_doc_btn.click(
|
| 478 |
+
fn=lambda files: text_to_speech_full(
|
| 479 |
+
"\n\n".join([extract_content(f) for f in (files if isinstance(files, list) else [files] if files else [])]),
|
| 480 |
+
1.0, 1.0
|
| 481 |
),
|
| 482 |
+
inputs=pdf_input,
|
|
|
|
| 483 |
outputs=read_doc_audio
|
| 484 |
)
|
| 485 |
|
| 486 |
+
# Document generation (use analysis_state)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 487 |
gen_letter.click(fn=lambda s: generate_letter(*s), inputs=analysis_state, outputs=doc_output)
|
| 488 |
gen_complaint.click(fn=lambda s: generate_complaint(*s), inputs=analysis_state, outputs=doc_output)
|
| 489 |
gen_witness.click(fn=lambda s: generate_witness(*s), inputs=analysis_state, outputs=doc_output)
|
|
|
|
| 495 |
</div>""")
|
| 496 |
|
| 497 |
if __name__ == "__main__":
|
| 498 |
+
demo.launch()
|