WaysAhead Global commited on
Commit
ee20dc3
·
verified ·
1 Parent(s): 40f4fea

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +98 -120
app.py CHANGED
@@ -17,6 +17,25 @@ import boto3
17
  from io import BytesIO
18
  import base64
19
  from langchain.chains import RetrievalQA
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  # ------------------- FastAPI Init -------------------
21
  app = FastAPI()
22
 
@@ -350,132 +369,89 @@ async def ask(payload: AskRequest):
350
 
351
 
352
  @app.post("/get_images")
353
- async def get_images(question: str = Form(...), answer: str = Form(...)):
354
- """
355
- Detects people (PERSON) and brands/companies (ORG) from both the question and answer.
356
- Returns both local DB-matched base64 images and web photos from DuckDuckGo.
357
-
358
- Output:
359
- {
360
- "images": [
361
- {
362
- "name": "Gopal Asthana",
363
- "local_photos": ["data:image/jpeg;base64,..."],
364
- "web_photos": ["https://..."]
365
- },
366
- ...
367
- ]
368
- }
369
- """
370
- try:
371
- # 🔍 NER over question and answer separately
372
- doc_q = nlp(question.strip())
373
- doc_a = nlp(answer.strip())
374
-
375
- entities = [
376
- ent.text.strip()
377
- for ent in list(doc_q.ents) + list(doc_a.ents)
378
- if ent.label_ in {"PERSON", "ORG"}
379
- ]
380
 
381
- # Fallback: Use n-gram tokens if spaCy fails
382
- if not entities and len(answer.strip().split()) >= 2:
383
- words = answer.strip().split()
384
- entities = [' '.join(words[i:i+2]) for i in range(len(words)-1)]
385
-
386
- seen = set()
387
- results = []
388
-
389
- for name in entities:
390
- norm_key = name.lower().replace(" ", "")
391
- if norm_key in seen:
392
- continue
393
- seen.add(norm_key)
394
-
395
- # ----- Local image base64 from DB -----
396
- local_photos = []
397
- try:
398
- conn = psycopg2.connect(
399
- host="45.194.46.142",
400
- port=5432,
401
- dbname="IMDL_DWH_DEV",
402
- user="imdl_dwh_da_user",
403
- password="ImagesDWH@2025"
404
- )
405
- cursor = conn.cursor()
406
- cursor.execute("""
407
- SELECT image_data FROM tb_fact_image_uploads
408
- WHERE LOWER(title) ILIKE %s
409
- AND is_active = true AND is_deleted = false
410
- LIMIT 1
411
- """, (f"%{name.lower()}%",))
412
- row = cursor.fetchone()
413
- cursor.close()
414
- conn.close()
415
-
416
- if row and row[0]:
417
- image_bytes = BytesIO(row[0]).getvalue()
418
- image_base64 = base64.b64encode(image_bytes).decode("utf-8")
419
- local_photos = [f"data:image/jpeg;base64,{image_base64}"]
420
-
421
- except Exception as e:
422
- logging.warning(f"⚠️ Local photo DB error for '{name}': {e}")
423
-
424
- # ----- Web fallback -----
425
- web_photos = []
426
- try:
427
- with DDGS() as ddgs:
428
- for r in ddgs.images(name, max_results=3):
429
- web_photos.append(r["image"])
430
- except Exception as e:
431
- logging.warning(f"⚠️ DuckDuckGo search failed for '{name}': {e}")
432
-
433
- results.append({
434
- "name": name,
435
- "local_photos": local_photos,
436
- "web_photos": web_photos
437
- })
438
-
439
- return {"images": results}
440
 
441
- except Exception as e:
442
- return JSONResponse(status_code=500, content={"error": f"Image detection failed: {e}"})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
443
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
444
 
445
 
446
  @app.post("/get_videos")
447
- async def get_videos(question: str = Form(...)):
448
  try:
449
- return {"videos": await asyncio.to_thread(fetch_youtube_videos.invoke, {"query": query})}
 
 
450
  except Exception as e:
451
  return JSONResponse(status_code=500, content={"error": f"Video fetch failed: {e}"})
452
 
453
 
454
  @app.post("/get_sources")
455
- async def get_sources(question: str = Form(...)):
456
- """
457
- Retrieve source metadata (filename, page, snippet, signed S3 URL)
458
- from the vector store using FAISS similarity search.
459
 
460
- Returns:
461
- {
462
- "sources": [
463
- {
464
- "source": "RetailSummit2024.pdf",
465
- "page": "3",
466
- "snippet": "Gopal Asthana presented innovations in fashion...",
467
- "signed_url": "https://..."
468
- },
469
- ...
470
- ]
471
- }
472
- """
473
- try:
474
- def clean(text):
475
- text = re.sub(r'\[([^\]]+)\]\((https?://[^\)]+)\)', r'\1', text)
476
- text = re.sub(r'https?://\S+', '', text)
477
- return text.strip()
478
 
 
479
  if not vector_store:
480
  return JSONResponse(status_code=500, content={"error": "FAISS vector store not initialized."})
481
 
@@ -483,7 +459,8 @@ async def get_sources(question: str = Form(...)):
483
  if not docs:
484
  return {"sources": []}
485
 
486
- seen = {}
 
487
  for doc in docs:
488
  source = doc.metadata.get("file_name", doc.metadata.get("source", "Unknown"))
489
  page = str(doc.metadata.get("page", "1"))
@@ -500,21 +477,22 @@ async def get_sources(question: str = Form(...)):
500
  return {"sources": list(seen.values())}
501
 
502
  except Exception as e:
503
- logging.error(f"❌ Error in get_sources: {e}")
504
  return JSONResponse(status_code=500, content={"error": f"Failed to fetch sources: {e}"})
505
 
506
 
507
 
508
  @app.post("/get_insights")
509
- async def get_insights(answer: str = Form(...), question: str = Form(...)):
510
  try:
511
- people = await asyncio.to_thread(detect_people_and_images.invoke, {"answer": answer})
512
- docs = await asyncio.to_thread(retrieve_documents.invoke, {"query": question})
513
  insights = []
514
  if people:
515
  insights.extend([f"{p['name']} was detected." for p in people])
516
- if docs:
517
- insights.append(f"{len(docs)} documents used. Top: {docs[0]['source']}")
518
  return {"insights": insights}
519
  except Exception as e:
520
- return JSONResponse(status_code=500, content={"error": f"Insight gen failed: {e}"})
 
 
17
  from io import BytesIO
18
  import base64
19
  from langchain.chains import RetrievalQA
20
+
21
+
22
+ class AskRequest(BaseModel):
23
+ question: str
24
+
25
+ class ImageRequest(BaseModel):
26
+ question: str
27
+ answer: str
28
+
29
+ class VideoRequest(BaseModel):
30
+ question: str
31
+
32
+ class SourceRequest(BaseModel):
33
+ question: str
34
+
35
+ class InsightRequest(BaseModel):
36
+ question: str
37
+ answer: str
38
+
39
  # ------------------- FastAPI Init -------------------
40
  app = FastAPI()
41
 
 
369
 
370
 
371
  @app.post("/get_images")
372
+ async def get_images(payload: ImageRequest):
373
+ question = payload.question
374
+ answer = payload.answer
375
+
376
+ doc_q = nlp(question.strip())
377
+ doc_a = nlp(answer.strip())
378
+ entities = [
379
+ ent.text.strip()
380
+ for ent in list(doc_q.ents) + list(doc_a.ents)
381
+ if ent.label_ in {"PERSON", "ORG"}
382
+ ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
 
384
+ if not entities and len(answer.strip().split()) >= 2:
385
+ words = answer.strip().split()
386
+ entities = [' '.join(words[i:i+2]) for i in range(len(words)-1)]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
387
 
388
+ seen, results = set(), []
389
+
390
+ for name in entities:
391
+ norm_key = name.lower().replace(" ", "")
392
+ if norm_key in seen:
393
+ continue
394
+ seen.add(norm_key)
395
+
396
+ # Local DB base64 image
397
+ local_photos = []
398
+ try:
399
+ conn = psycopg2.connect(**DB_CONFIG)
400
+ cursor = conn.cursor()
401
+ cursor.execute("""
402
+ SELECT image_data FROM tb_fact_image_uploads
403
+ WHERE LOWER(title) ILIKE %s
404
+ AND is_active = true AND is_deleted = false
405
+ LIMIT 1
406
+ """, (f"%{name.lower()}%",))
407
+ row = cursor.fetchone()
408
+ cursor.close()
409
+ conn.close()
410
+ if row and row[0]:
411
+ image_bytes = BytesIO(row[0]).getvalue()
412
+ image_base64 = base64.b64encode(image_bytes).decode("utf-8")
413
+ local_photos = [f"data:image/jpeg;base64,{image_base64}"]
414
+ except Exception as e:
415
+ logging.warning(f"Local photo DB error for '{name}': {e}")
416
 
417
+ # Web fallback
418
+ web_photos = []
419
+ try:
420
+ with DDGS() as ddgs:
421
+ for r in ddgs.images(name, max_results=3):
422
+ web_photos.append(r["image"])
423
+ except Exception as e:
424
+ logging.warning(f"DuckDuckGo failed for '{name}': {e}")
425
+
426
+ results.append({
427
+ "name": name,
428
+ "local_photos": local_photos,
429
+ "web_photos": web_photos
430
+ })
431
+
432
+ return {"images": results}
433
 
434
 
435
  @app.post("/get_videos")
436
+ async def get_videos(payload: VideoRequest):
437
  try:
438
+ return {
439
+ "videos": await asyncio.to_thread(fetch_youtube_videos.invoke, {"question": payload.question})
440
+ }
441
  except Exception as e:
442
  return JSONResponse(status_code=500, content={"error": f"Video fetch failed: {e}"})
443
 
444
 
445
  @app.post("/get_sources")
446
+ async def get_sources(payload: SourceRequest):
447
+ question = payload.question
 
 
448
 
449
+ def clean(text):
450
+ text = re.sub(r'\[([^\]]+)\]\((https?://[^\)]+)\)', r'\1', text)
451
+ text = re.sub(r'https?://\S+', '', text)
452
+ return text.strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
453
 
454
+ try:
455
  if not vector_store:
456
  return JSONResponse(status_code=500, content={"error": "FAISS vector store not initialized."})
457
 
 
459
  if not docs:
460
  return {"sources": []}
461
 
462
+ seen, sources = {}, []
463
+
464
  for doc in docs:
465
  source = doc.metadata.get("file_name", doc.metadata.get("source", "Unknown"))
466
  page = str(doc.metadata.get("page", "1"))
 
477
  return {"sources": list(seen.values())}
478
 
479
  except Exception as e:
480
+ logging.error(f"Error in get_sources: {e}")
481
  return JSONResponse(status_code=500, content={"error": f"Failed to fetch sources: {e}"})
482
 
483
 
484
 
485
  @app.post("/get_insights")
486
+ async def get_insights(payload: InsightRequest):
487
  try:
488
+ people = await asyncio.to_thread(detect_people_and_images.invoke, {"answer": payload.answer})
489
+ docs = await asyncio.to_thread(retrieve_documents.invoke, {"query": payload.question})
490
  insights = []
491
  if people:
492
  insights.extend([f"{p['name']} was detected." for p in people])
493
+ if isinstance(docs, str) and docs.strip():
494
+ insights.append("Document-based answer was successfully generated.")
495
  return {"insights": insights}
496
  except Exception as e:
497
+ return JSONResponse(status_code=500, content={"error": f"Insight generation failed: {e}"})
498
+