fixed response
This commit is contained in:
parent
9be0343695
commit
15a0061a0d
6 changed files with 107 additions and 60 deletions
Binary file not shown.
|
|
@ -1,13 +1,17 @@
|
|||
from fastapi import APIRouter, HTTPException
|
||||
import httpx
|
||||
from typing import Union
|
||||
from app.config import GOOGLE_API_KEY, GOOGLE_FACT_CHECK_BASE_URL, OPENAI_API_KEY
|
||||
from app.api.scrap_websites import search_websites, SearchRequest
|
||||
from app.services.openai_client import OpenAIClient
|
||||
from app.models.fact_check_models import (
|
||||
FactCheckRequest,
|
||||
FactCheckResponse,
|
||||
UnverifiedFactCheckResponse,
|
||||
ErrorResponse,
|
||||
Source,
|
||||
VerdictEnum,
|
||||
ConfidenceEnum
|
||||
)
|
||||
from app.websites.fact_checker_website import get_all_sources
|
||||
|
||||
|
|
@ -15,7 +19,7 @@ fact_check_router = APIRouter()
|
|||
openai_client = OpenAIClient(OPENAI_API_KEY)
|
||||
|
||||
|
||||
async def generate_fact_report(query: str, fact_check_data: dict) -> FactCheckResponse:
|
||||
async def generate_fact_report(query: str, fact_check_data: dict) -> Union[FactCheckResponse, UnverifiedFactCheckResponse]:
|
||||
"""Generate a fact check report using OpenAI based on the fact check results."""
|
||||
try:
|
||||
base_system_prompt = """You are a professional fact-checking reporter. Your task is to create a detailed fact check report based on the provided data. Focus on accuracy, clarity, and proper citation of sources.
|
||||
|
|
@ -24,7 +28,24 @@ Rules:
|
|||
1. Include all source URLs and names in the sources list
|
||||
2. Keep the explanation focused on verifiable facts
|
||||
3. Include dates when available
|
||||
4. Maintain objectivity in the report"""
|
||||
4. Maintain objectivity in the report
|
||||
5. If no reliable sources are found, provide a clear explanation why"""
|
||||
|
||||
# If no sources were found, return an unverified response
|
||||
if not fact_check_data.get("claims") and (
|
||||
not fact_check_data.get("urls_found") or
|
||||
fact_check_data.get("status") == "no_results" or
|
||||
fact_check_data.get("verification_result", {}).get("no_sources_found")
|
||||
):
|
||||
return UnverifiedFactCheckResponse(
|
||||
claim=query,
|
||||
verdict=VerdictEnum.UNVERIFIED,
|
||||
confidence=ConfidenceEnum.LOW,
|
||||
sources=[],
|
||||
evidence="No fact-checking sources have verified this claim yet.",
|
||||
explanation="Our search across reputable fact-checking websites did not find any formal verification of this claim. This doesn't mean the claim is false - just that it hasn't been formally fact-checked yet.",
|
||||
additional_context="The claim may be too recent for fact-checkers to have investigated, or it may not have been widely circulated enough to warrant formal fact-checking."
|
||||
)
|
||||
|
||||
base_user_prompt = """Generate a comprehensive fact check report in this exact JSON format:
|
||||
{
|
||||
|
|
@ -40,9 +61,7 @@ Rules:
|
|||
"evidence": "A concise summary of the key evidence (1-2 sentences)",
|
||||
"explanation": "A detailed explanation including who verified it, when it was verified, and the key findings (2-3 sentences)",
|
||||
"additional_context": "Important context about the verification process, limitations, or broader implications (1-2 sentences)"
|
||||
}
|
||||
|
||||
Ensure all URLs in sources are complete (including https:// if missing) and each source has both a URL and name."""
|
||||
}"""
|
||||
|
||||
if "claims" in fact_check_data:
|
||||
system_prompt = base_system_prompt
|
||||
|
|
@ -71,75 +90,74 @@ Ensure all URLs in sources are complete (including https:// if missing) and each
|
|||
4. Note any conflicting information between sources"""
|
||||
|
||||
response = await openai_client.generate_text_response(
|
||||
system_prompt=system_prompt, user_prompt=user_prompt, max_tokens=1000
|
||||
system_prompt=system_prompt,
|
||||
user_prompt=user_prompt,
|
||||
max_tokens=1000
|
||||
)
|
||||
|
||||
try:
|
||||
# First try to parse the response directly
|
||||
response_data = response["response"]
|
||||
|
||||
# Clean up sources before validation
|
||||
if isinstance(response_data.get("sources"), list):
|
||||
cleaned_sources = []
|
||||
for source in response_data["sources"]:
|
||||
if isinstance(source, str):
|
||||
# Convert string sources to Source objects
|
||||
url = (
|
||||
source if source.startswith("http") else f"https://{source}"
|
||||
)
|
||||
url = source if source.startswith("http") else f"https://{source}"
|
||||
cleaned_sources.append({"url": url, "name": source})
|
||||
elif isinstance(source, dict):
|
||||
# Ensure URL has proper scheme
|
||||
url = source.get("url", "")
|
||||
if url and not url.startswith("http"):
|
||||
source["url"] = f"https://{url}"
|
||||
cleaned_sources.append(source)
|
||||
response_data["sources"] = cleaned_sources
|
||||
|
||||
fact_check_response = FactCheckResponse(**response_data)
|
||||
return fact_check_response
|
||||
if response_data["verdict"] == "Unverified" or not response_data.get("sources"):
|
||||
return UnverifiedFactCheckResponse(**response_data)
|
||||
return FactCheckResponse(**response_data)
|
||||
|
||||
except Exception as validation_error:
|
||||
print(f"Response validation error: {str(validation_error)}")
|
||||
raise HTTPException(
|
||||
status_code=422,
|
||||
detail=ErrorResponse(
|
||||
detail=f"Invalid response format: {str(validation_error)}",
|
||||
error_code="VALIDATION_ERROR",
|
||||
path="/check-facts",
|
||||
).dict(),
|
||||
return UnverifiedFactCheckResponse(
|
||||
claim=query,
|
||||
verdict=VerdictEnum.UNVERIFIED,
|
||||
confidence=ConfidenceEnum.LOW,
|
||||
sources=[],
|
||||
evidence="An error occurred while processing the fact check results.",
|
||||
explanation="The system encountered an error while validating the fact check results.",
|
||||
additional_context="This is a technical error and does not reflect on the truthfulness of the claim."
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error generating fact report: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=ErrorResponse(
|
||||
detail="Error generating fact report",
|
||||
error_code="FACT_CHECK_ERROR",
|
||||
path="/check-facts",
|
||||
).dict(),
|
||||
return UnverifiedFactCheckResponse(
|
||||
claim=query,
|
||||
verdict=VerdictEnum.UNVERIFIED,
|
||||
confidence=ConfidenceEnum.LOW,
|
||||
sources=[],
|
||||
evidence="An error occurred while generating the fact check report.",
|
||||
explanation="The system encountered an unexpected error while processing the fact check request.",
|
||||
additional_context="This is a technical error and does not reflect on the truthfulness of the claim."
|
||||
)
|
||||
|
||||
|
||||
@fact_check_router.post("/check-facts", response_model=FactCheckResponse)
|
||||
@fact_check_router.post("/check-facts", response_model=Union[FactCheckResponse, UnverifiedFactCheckResponse])
|
||||
async def check_facts(request: FactCheckRequest):
|
||||
"""
|
||||
Fetch fact check results and generate a comprehensive report.
|
||||
"""
|
||||
if not GOOGLE_API_KEY or not GOOGLE_FACT_CHECK_BASE_URL:
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=ErrorResponse(
|
||||
detail="Google API key or base URL is not configured",
|
||||
error_code="CONFIGURATION_ERROR",
|
||||
path="/check-facts",
|
||||
).dict(),
|
||||
return UnverifiedFactCheckResponse(
|
||||
claim=request.query,
|
||||
verdict=VerdictEnum.UNVERIFIED,
|
||||
confidence=ConfidenceEnum.LOW,
|
||||
sources=[],
|
||||
evidence="The fact-checking service is not properly configured.",
|
||||
explanation="The system is missing required API configuration for fact-checking services.",
|
||||
additional_context="This is a temporary system configuration issue."
|
||||
)
|
||||
|
||||
headers = {"Content-Type": "application/json"}
|
||||
async with httpx.AsyncClient() as client:
|
||||
# Get fact checker sources from the centralized configuration
|
||||
fact_checker_sources = get_all_sources()
|
||||
|
||||
for source in fact_checker_sources:
|
||||
|
|
@ -170,7 +188,8 @@ async def check_facts(request: FactCheckRequest):
|
|||
|
||||
try:
|
||||
search_request = SearchRequest(
|
||||
search_text=request.query, source_types=["fact_checkers"]
|
||||
search_text=request.query,
|
||||
source_types=["fact_checkers"]
|
||||
)
|
||||
|
||||
ai_response = await search_websites(search_request)
|
||||
|
|
@ -178,11 +197,10 @@ async def check_facts(request: FactCheckRequest):
|
|||
|
||||
except Exception as e:
|
||||
print(f"Error in AI fact check: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=ErrorResponse(
|
||||
detail="No fact check results found",
|
||||
error_code="NOT_FOUND",
|
||||
path="/check-facts",
|
||||
).dict(),
|
||||
)
|
||||
return await generate_fact_report(request.query, {
|
||||
"status": "no_results",
|
||||
"verification_result": {
|
||||
"no_sources_found": True,
|
||||
"reason": str(e)
|
||||
}
|
||||
})
|
||||
Binary file not shown.
|
|
@ -33,12 +33,44 @@ class Source(BaseModel):
|
|||
|
||||
@validator("url")
|
||||
def validate_url(cls, v):
|
||||
# Basic URL validation without requiring HTTP/HTTPS
|
||||
if not v or len(v) < 3:
|
||||
raise ValueError("URL must not be empty and must be at least 3 characters")
|
||||
return v
|
||||
|
||||
|
||||
class UnverifiedFactCheckResponse(BaseModel):
|
||||
claim: str = Field(
|
||||
...,
|
||||
min_length=10,
|
||||
max_length=1000,
|
||||
description="The exact claim being verified",
|
||||
)
|
||||
verdict: VerdictEnum = Field(..., description="The verification verdict")
|
||||
confidence: ConfidenceEnum = Field(..., description="Confidence level in the verdict")
|
||||
sources: List[Source] = Field(
|
||||
default=[],
|
||||
description="List of sources used in verification"
|
||||
)
|
||||
evidence: str = Field(
|
||||
...,
|
||||
min_length=20,
|
||||
max_length=500,
|
||||
description="Concise summary of key evidence",
|
||||
)
|
||||
explanation: str = Field(
|
||||
...,
|
||||
min_length=50,
|
||||
max_length=1000,
|
||||
description="Detailed explanation of verification findings",
|
||||
)
|
||||
additional_context: str = Field(
|
||||
...,
|
||||
min_length=20,
|
||||
max_length=500,
|
||||
description="Important context about the verification",
|
||||
)
|
||||
|
||||
|
||||
class FactCheckResponse(BaseModel):
|
||||
claim: str = Field(
|
||||
...,
|
||||
|
|
@ -47,11 +79,11 @@ class FactCheckResponse(BaseModel):
|
|||
description="The exact claim being verified",
|
||||
)
|
||||
verdict: VerdictEnum = Field(..., description="The verification verdict")
|
||||
confidence: ConfidenceEnum = Field(
|
||||
..., description="Confidence level in the verdict"
|
||||
)
|
||||
confidence: ConfidenceEnum = Field(..., description="Confidence level in the verdict")
|
||||
sources: List[Source] = Field(
|
||||
..., min_items=1, description="List of sources used in verification"
|
||||
...,
|
||||
min_items=1,
|
||||
description="List of sources used in verification"
|
||||
)
|
||||
evidence: str = Field(
|
||||
...,
|
||||
|
|
@ -82,15 +114,11 @@ class FactCheckResponse(BaseModel):
|
|||
{
|
||||
"url": "https://www.nasa.gov/mars-exploration",
|
||||
"name": "NASA Mars Exploration",
|
||||
},
|
||||
{
|
||||
"url": "https://factcheck.org/2024/mars-claims",
|
||||
"name": "FactCheck.org",
|
||||
},
|
||||
}
|
||||
],
|
||||
"evidence": "NASA has made no such announcement. Recent Mars rover images show natural rock formations.",
|
||||
"explanation": "Multiple fact-checking organizations investigated this claim. NASA's official communications and Mars mission reports from 2024 contain no mention of alien structures. The viral images being shared are misidentified natural geological formations.",
|
||||
"additional_context": "Similar false claims about alien structures on Mars have circulated periodically since the first Mars rovers began sending back images.",
|
||||
"explanation": "Multiple fact-checking organizations investigated this claim. NASA's official communications and Mars mission reports from 2024 contain no mention of alien structures.",
|
||||
"additional_context": "Similar false claims about alien structures on Mars have circulated periodically.",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
BIN
images-test.jpg
Normal file
BIN
images-test.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
1
main.py
1
main.py
|
|
@ -17,6 +17,7 @@ origins = [
|
|||
"http://localhost:5173",
|
||||
"http://0.0.0.0",
|
||||
"http://0.0.0.0:5173",
|
||||
"*"
|
||||
]
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue