Spaces:
Sleeping
Sleeping
| import sys | |
| import os | |
| import json | |
| from concurrent.futures import ThreadPoolExecutor | |
| sys.path.insert(0, os.path.join(os.path.dirname(__file__), "app")) | |
| from fastapi import FastAPI, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from pydantic import BaseModel | |
| from dotenv import load_dotenv | |
| import redis | |
| load_dotenv() | |
| from retrieval import get_similar_papers | |
| from verify import get_verdict | |
| from agents.judge import judge_papers | |
| from agents.decomposer import decompose_claim | |
| from agents.explainer import explain_verdict | |
| app = FastAPI(title="ClearVoice API", version="2.0.0") | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| cache = redis.Redis.from_url(os.getenv("REDIS_URL"), decode_responses=True) | |
| CACHE_TTL = 60 * 60 * 24 | |
| class ClaimRequest(BaseModel): | |
| claim: str | |
| def determine_final_verdict(verdict: dict, judge_result: dict) -> dict: | |
| papers = judge_result.get("papers", []) | |
| supports = sum(1 for p in papers if p.get("stance") == "SUPPORTS") | |
| contradicts = sum(1 for p in papers if p.get("stance") == "CONTRADICTS") | |
| if supports > 0 and contradicts > 0: | |
| verdict["verdict"] = "MISLEADING" | |
| verdict["confidence"] = min(verdict.get("confidence", 0.5), 0.7) | |
| verdict["explanation"] = verdict.get("explanation", "") + f" Note: Evidence is mixed — {supports} paper(s) support and {contradicts} paper(s) contradict this claim." | |
| return verdict | |
| def root(): | |
| return {"status": "ClearVoice API is running", "version": "2.0.0"} | |
| def health(): | |
| try: | |
| cache.ping() | |
| redis_status = "ok" | |
| except Exception: | |
| redis_status = "unavailable" | |
| return {"status": "ok", "redis": redis_status} | |
| def verify_claim(request: ClaimRequest): | |
| claim = request.claim.strip() | |
| if not claim: | |
| raise HTTPException(status_code=400, detail="Claim cannot be empty") | |
| cached = cache.get(claim) | |
| if cached: | |
| result = json.loads(cached) | |
| result["cached"] = True | |
| return result | |
| if " and " in claim.lower() or " or " in claim.lower(): | |
| decomposition = decompose_claim(claim) | |
| else: | |
| decomposition = {"is_complex": False, "sub_claims": [claim], "original_claim": claim} | |
| primary_claim = decomposition["sub_claims"][0] | |
| papers = get_similar_papers(primary_claim) | |
| if not papers: | |
| return { | |
| "verdict": "INSUFFICIENT EVIDENCE", | |
| "confidence": 0.0, | |
| "explanation": "No relevant peer-reviewed studies found for this claim.", | |
| "citations": [], | |
| "papers": [], | |
| "judge": {}, | |
| "decomposition": decomposition, | |
| "plain_english": "We couldn't find any relevant medical studies to evaluate this claim.", | |
| "takeaway": "Consult a medical professional for guidance.", | |
| "evidence_strength": "Insufficient", | |
| "cached": False | |
| } | |
| with ThreadPoolExecutor(max_workers=2) as executor: | |
| verdict_future = executor.submit(get_verdict, primary_claim, papers) | |
| judge_future = executor.submit(judge_papers, primary_claim, papers[:3]) | |
| verdict = verdict_future.result() | |
| judge_result = judge_future.result() | |
| verdict = determine_final_verdict(verdict, judge_result) | |
| explanation_result = explain_verdict( | |
| claim=claim, | |
| verdict=verdict.get("verdict", ""), | |
| confidence=verdict.get("confidence", 0), | |
| explanation=verdict.get("explanation", ""), | |
| judge_result=judge_result | |
| ) | |
| verdict["papers"] = [ | |
| { | |
| "pmid": p["pmid"], | |
| "title": p["title"], | |
| "journal": p["journal"], | |
| "year": p["year"], | |
| "similarity": round(float(p["similarity"]), 2) | |
| } | |
| for p in papers | |
| ] | |
| verdict["judge"] = judge_result | |
| verdict["decomposition"] = decomposition | |
| verdict["plain_english"] = explanation_result.get("plain_english", "") | |
| verdict["takeaway"] = explanation_result.get("takeaway", "") | |
| verdict["evidence_strength"] = explanation_result.get("evidence_strength", "") | |
| verdict["cached"] = False | |
| cache.setex(claim, CACHE_TTL, json.dumps(verdict)) | |
| return verdict |