Basementup commited on
Commit
fb2ff74
·
verified ·
1 Parent(s): 071f2e7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +61 -43
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(4):
35
  try:
36
- fmt = prompt
37
  resp = _req.post(
38
  _FREE_API.format(model=model),
39
  headers=headers,
40
  json={
41
- "inputs": fmt,
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=180
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(30 * (attempt + 1))
60
  continue
61
  elif resp.status_code in [429, 402]:
62
  break
63
  else:
64
- time.sleep(10)
65
  continue
66
- except Exception:
67
- time.sleep(10)
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
- return Path(path).read_text(encoding="utf-8", errors="replace")
 
 
 
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"FACT LEDGER:\n{r1[:1500]}\n\n"
200
- f"BREACH ANALYSIS:\n{r2[:1500]}\n\n"
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 = asyncio.new_event_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
- loop.close()
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 (short versions for demo)
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 &amp; Owned by Dwayne Anthony Brian Galloway &nbsp;·&nbsp; 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
- # Helper to bind analysis outputs for generation
437
- def _get_outputs(r1, r2, r3, r4):
438
- return r1, r2, r3, r4
439
 
440
- # Analyse
441
  analyse_btn.click(
442
- fn=analyse,
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, speed, pitch: text_to_speech_full(
456
- "\n\n".join([extract_content(f) for f in (files if isinstance(files, list) else [files])]),
457
- speed, pitch
458
  ),
459
- inputs=[pdf_input, gr.Slider(0.5, 2.0, value=1.0, label="Speed"),
460
- gr.Slider(0.5, 2.0, value=1.0, label="Pitch")],
461
  outputs=read_doc_audio
462
  )
463
 
464
- # Document generation (bind analysis outputs as state)
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 &amp; Owned by Dwayne Anthony Brian Galloway &nbsp;·&nbsp; 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()