Trololindo commited on
Commit
c56dba2
·
1 Parent(s): 8a8571b

Bóg wie co z tego bedzie

Browse files
backend/app/api/factcheck_router.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, HTTPException
2
+ from app.models.factcheck_schemas import FactCheckRequest, FactCheckResponse, FactCheckSource
3
+ from app.services.factcheck_service import search_web, analyze_with_gemini
4
+
5
+ router = APIRouter()
6
+
7
+ @router.post(
8
+ "/factcheck",
9
+ response_model=FactCheckResponse,
10
+ tags=["Fact-checking"],
11
+ summary="Zweryfikuj prawdziwość stwierdzenia"
12
+ )
13
+ async def fact_check_endpoint(payload: FactCheckRequest):
14
+ statement = payload.statement.strip()
15
+ if len(statement) < 10:
16
+ raise HTTPException(status_code=400, detail="Tekst do weryfikacji musi mieć co najmniej 10 znaków.")
17
+
18
+ # 1. Przeszukiwanie sieci
19
+ web_results = search_web(statement, max_results=5)
20
+ if not web_results:
21
+ return FactCheckResponse(
22
+ verdict="SPORNE",
23
+ explanation="Wyszukiwarka nie zwróciła żadnych wyników w internecie dla tego stwierdzenia, co uniemożliwia weryfikację.",
24
+ confidence=0.0,
25
+ sources=[]
26
+ )
27
+
28
+ # 2. Analiza przez LLM
29
+ analysis = await analyze_with_gemini(statement, web_results)
30
+
31
+ # 3. Przypisanie źródeł na podstawie decyzji LLM
32
+ used_indices = analysis.get("sources_used_indices", [])
33
+ used_sources = []
34
+
35
+ for idx in used_indices:
36
+ source_idx = idx - 1 # Korekta indeksu (model liczy od 1)
37
+ if 0 <= source_idx < len(web_results):
38
+ r = web_results[source_idx]
39
+ used_sources.append(FactCheckSource(
40
+ title=r["title"],
41
+ url=r["url"],
42
+ snippet=r["snippet"]
43
+ ))
44
+
45
+ # Jeśli model nie wskazał konkretnych indeksów, dajemy top 3 znalezione źródła
46
+ if not used_sources:
47
+ used_sources = [
48
+ FactCheckSource(title=r["title"], url=r["url"], snippet=r["snippet"])
49
+ for r in web_results[:3]
50
+ ]
51
+
52
+ return FactCheckResponse(
53
+ verdict=analysis.get("verdict", "SPORNE"),
54
+ explanation=analysis.get("explanation", "Brak szczegółowego uzasadnienia."),
55
+ confidence=analysis.get("confidence", 0.5),
56
+ sources=used_sources
57
+ )
backend/app/api/routes.py CHANGED
@@ -154,4 +154,7 @@ async def analyze(request: Request, payload: AnalysisRequest) -> AnalysisRespons
154
  analysis_time=analysis_result["analysis_time"],
155
  used_model=model,
156
  content_type=content_type,
157
- )
 
 
 
 
154
  analysis_time=analysis_result["analysis_time"],
155
  used_model=model,
156
  content_type=content_type,
157
+ )
158
+
159
+ from app.api.factcheck_router import router as factcheck_router
160
+ router.include_router(factcheck_router) #kupczak tu był
backend/app/models/factcheck_schemas.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel
2
+ from typing import List
3
+
4
+ class FactCheckRequest(BaseModel):
5
+ statement: str
6
+
7
+ class FactCheckSource(BaseModel):
8
+ title: str
9
+ url: str
10
+ snippet: str
11
+
12
+ class FactCheckResponse(BaseModel):
13
+ verdict: str # "PRAWDA", "FAŁSZ", "SPORNE"
14
+ explanation: str
15
+ confidence: float
16
+ sources: List[FactCheckSource]
backend/app/services/factcheck_service.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import json
3
+ import re
4
+ import os
5
+ from typing import Dict, Any, List
6
+ from duckduckgo_search import DDGS
7
+ import google.generativeai as genai
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+ def search_web(query: str, max_results: int = 5) -> List[Dict[str, str]]:
12
+ """Przeszukuje internet bez limitów i bez kluczy API za pomocą DuckDuckGo."""
13
+ logger.info(f"Wyszukiwanie w sieci dla zapytania: {query}")
14
+ try:
15
+ with DDGS() as ddgs:
16
+ results = ddgs.text(query, max_results=max_results)
17
+ formatted_results = []
18
+ for r in results:
19
+ formatted_results.append({
20
+ "title": r.get("title", "Brak tytułu"),
21
+ "url": r.get("href", ""),
22
+ "snippet": r.get("body", "Brak opisu")
23
+ })
24
+ return formatted_results
25
+ except Exception as e:
26
+ logger.error(f"Błąd wyszukiwania DuckDuckGo: {e}", exc_info=True)
27
+ return []
28
+
29
+ async def analyze_with_gemini(statement: str, sources: List[Dict[str, str]]) -> Dict[str, Any]:
30
+ """Analizuje stwierdzenie na podstawie wyników wyszukiwania za pomocą Gemini API."""
31
+ # Pobieramy klucz bezpośrednio ze środowiska lub .env
32
+ api_key = os.getenv("GEMINI_API_KEY")
33
+
34
+ if not api_key:
35
+ logger.error("Brak klucza GEMINI_API_KEY w środowisku systemowym!")
36
+ return {
37
+ "verdict": "SPORNE",
38
+ "explanation": "Błąd backendu: Brak skonfigurowanego klucza GEMINI_API_KEY w pliku .env.",
39
+ "confidence": 0.0,
40
+ "sources_used_indices": []
41
+ }
42
+
43
+ genai.configure(api_key=api_key)
44
+
45
+ # Przygotowanie czytelnego tekstu ze źródłami dla LLM
46
+ sources_text = ""
47
+ for idx, s in enumerate(sources, start=1):
48
+ sources_text += f"[{idx}] Tytuł: {s['title']}\nURL: {s['url']}\nTreść: {s['snippet']}\n\n"
49
+
50
+ prompt = f"""Jesteś zaawansowanym asystentem do weryfikacji faktów (fact-checking).
51
+ Twoim zadaniem jest ocena, czy podane STWIERDZENIE jest prawdziwe, fałszywe czy sporne na podstawie dostarczonych WYNIKÓW WYSZUKIWANIA.
52
+
53
+ STWIERDZENIE DO WERYFIKACJI:
54
+ "{statement}"
55
+
56
+ WYNIKI WYSZUKIWANIA:
57
+ {sources_text}
58
+
59
+ Wygeneruj rzetelną analizę. Odpowiedz w języku polskim. Twoja odpowiedź MUSI być poprawnym, czystym obiektem JSON o następującym formacie (i niczym innym):
60
+ {{
61
+ "verdict": "PRAWDA" lub "FAŁSZ" lub "SPORNE",
62
+ "explanation": "Zwięzłe (2-4 zdania), merytoryczne i obiektywne uzasadnienie werdyktu w języku polskim wraz z odniesieniem do źródeł.",
63
+ "confidence": 0.85,
64
+ "sources_used_indices": [1, 3]
65
+ }}
66
+
67
+ Zasady oceny:
68
+ - "PRAWDA": Wyniki jednoznacznie potwierdzają to stwierdzenie.
69
+ - "FAŁSZ": Wyniki wykazują błąd, dezinformację lub bezpośrednio zaprzeczają stwierdzeniu.
70
+ - "SPORNE": Istnieją sprzeczne informacje, jest to kwestia opinii lub źródła nie dają jednoznacznej odpowiedzi.
71
+
72
+ Zwróć TYLKO czysty obiekt JSON. Nie dodawaj bloków kodu ```json ani żadnych komentarzy poza obiektem JSON."""
73
+
74
+ try:
75
+ model = genai.GenerativeModel("gemini-1.5-flash")
76
+ response = model.generate_content(
77
+ prompt,
78
+ generation_config=genai.types.GenerationConfig(
79
+ temperature=0.0, # Niska temperatura chroni przed zmyślaniem (hallucination)
80
+ response_mime_type="application/json"
81
+ )
82
+ )
83
+
84
+ raw_text = response.text.strip()
85
+
86
+ # Oczyszczenie formatowania markdown, gdyby model mimo wszystko go dodał
87
+ if raw_text.startswith("```"):
88
+ match = re.search(r"```(?:json)?\s*(\{.*?\})\s*```", raw_text, re.DOTALL)
89
+ if match:
90
+ raw_text = match.group(1)
91
+
92
+ return json.loads(raw_text)
93
+ except Exception as e:
94
+ logger.error(f"Błąd analizy Gemini API: {e}", exc_info=True)
95
+ return {
96
+ "verdict": "SPORNE",
97
+ "explanation": f"Wystąpił błąd komunikacji z modelem językowym: {str(e)}",
98
+ "confidence": 0.0,
99
+ "sources_used_indices": []
100
+ }
backend/requirements.txt CHANGED
@@ -12,4 +12,6 @@ protobuf
12
  Pillow
13
  slowapi
14
  pytest==7.4.3
15
- pytest-asyncio==0.21.1
 
 
 
12
  Pillow
13
  slowapi
14
  pytest==7.4.3
15
+ pytest-asyncio==0.21.1
16
+ duckduckgo-search>=6.0.0
17
+ google-generativeai>=0.8.0
index.js CHANGED
@@ -53,6 +53,10 @@ client.once(Events.ClientReady, async () => {
53
  {
54
  name: "Wykryj deepfake",
55
  type: ApplicationCommandType.Message
 
 
 
 
56
  }
57
  ]);
58
  console.log("Pomyślnie zarejestrowano komendy (/detect, /setup oraz menu kontekstowe)");
@@ -306,6 +310,8 @@ async function handleAnalysis(interaction, userContent, targetMessage = null, ex
306
  }
307
  }
308
 
 
 
309
  client.on(Events.InteractionCreate, async (interaction) => {
310
 
311
  if (interaction.isChatInputCommand()) {
@@ -410,8 +416,23 @@ client.on(Events.InteractionCreate, async (interaction) => {
410
 
411
  await handleAnalysis(interaction, contentToAnalyze, targetMessage, explicitContentType);
412
  }
413
- }
 
 
414
 
 
 
 
 
 
 
 
 
 
 
 
 
 
415
  if (interaction.isModalSubmit()) {
416
  if (interaction.customId === "detectModal") {
417
  const userContent = interaction.fields.getTextInputValue("detectInput");
 
53
  {
54
  name: "Wykryj deepfake",
55
  type: ApplicationCommandType.Message
56
+ },
57
+ {
58
+ name: "Weryfikacja faktów", // <--- TA LINIA
59
+ type: ApplicationCommandType.Message
60
  }
61
  ]);
62
  console.log("Pomyślnie zarejestrowano komendy (/detect, /setup oraz menu kontekstowe)");
 
310
  }
311
  }
312
 
313
+ //funkcja kupczaka
314
+
315
  client.on(Events.InteractionCreate, async (interaction) => {
316
 
317
  if (interaction.isChatInputCommand()) {
 
416
 
417
  await handleAnalysis(interaction, contentToAnalyze, targetMessage, explicitContentType);
418
  }
419
+ if (interaction.commandName === "Weryfikacja faktów") {
420
+ const targetMessage = interaction.targetMessage;
421
+ const contentToVerify = targetMessage.content;
422
 
423
+ if (!contentToVerify || contentToVerify.trim().length < 10) {
424
+ return interaction.reply({
425
+ content: "❌ Wiadomość musi mieć przynajmniej 10 znaków tekstu, aby można było ją zweryfikować.",
426
+ flags: [MessageFlags.Ephemeral]
427
+ });
428
+ }
429
+
430
+ await handleFactCheck(interaction, contentToVerify);
431
+ }
432
+
433
+ }
434
+ //koniec funkcji kupczaka
435
+
436
  if (interaction.isModalSubmit()) {
437
  if (interaction.customId === "detectModal") {
438
  const userContent = interaction.fields.getTextInputValue("detectInput");