Gradii commited on
Commit
c0426da
1 Parent(s): d5f9903

nowy model

Browse files
backend/app/api/routes.py CHANGED
@@ -5,6 +5,7 @@ from app.models.schemas import (
5
  AnalysisRequest,
6
  AnalysisResponse,
7
  ErrorResponse,
 
8
  HealthResponse,
9
  TextAnalysisRequest,
10
  ImageAnalysisRequest,
@@ -13,8 +14,9 @@ from app.services.download import download_file
13
  from app.services.text_analyzer import analyze_text
14
  from app.services.image_analyzer import analyze_image
15
  from app.core.config import get_settings
16
- from app.utils.exceptions import DeepfakeDetectionError
17
  from app.core.limiter import limiter
 
18
 
19
  logger = logging.getLogger(__name__)
20
 
@@ -56,8 +58,35 @@ async def health_check() -> HealthResponse:
56
  version=settings.APP_VERSION,
57
  available_models=settings.AVAILABLE_MODELS,
58
  supported_types=list(settings.AVAILABLE_MODELS.keys()),
 
59
  )
60
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  @router.post(
62
  "/analyze",
63
  response_model=AnalysisResponse,
 
5
  AnalysisRequest,
6
  AnalysisResponse,
7
  ErrorResponse,
8
+ GuildConfigSchema,
9
  HealthResponse,
10
  TextAnalysisRequest,
11
  ImageAnalysisRequest,
 
14
  from app.services.text_analyzer import analyze_text
15
  from app.services.image_analyzer import analyze_image
16
  from app.core.config import get_settings
17
+ from app.utils.exceptions import DeepfakeDetectionError, SetupRequiredError
18
  from app.core.limiter import limiter
19
+ from app.config_manager import save_guild_config
20
 
21
  logger = logging.getLogger(__name__)
22
 
 
58
  version=settings.APP_VERSION,
59
  available_models=settings.AVAILABLE_MODELS,
60
  supported_types=list(settings.AVAILABLE_MODELS.keys()),
61
+ models_status=models_status,
62
  )
63
 
64
+ # Endpoint do zapisywania konfiguracji (wywo艂ywany przez bota)
65
+ @router.post("/guilds/{guild_id}/setup", tags=["Setup"])
66
+ async def save_discord_guild_setup(guild_id: str, payload: GuildConfigSchema):
67
+ # Walidacja modeli z pliku ustawie艅
68
+ settings = get_settings()
69
+ allowed_text_models = settings.AVAILABLE_MODELS.get("text", [])
70
+
71
+ # Walidujemy tylko wtedy, gdy model nie jest ustawiony na "none"
72
+ if payload.active_text_model and payload.active_text_model.lower() != "none":
73
+ if payload.active_text_model not in allowed_text_models:
74
+ raise HTTPException(
75
+ status_code=400,
76
+ detail=f"Model '{payload.active_text_model}' nie jest dozwolony. Wybierz z: {allowed_text_models}"
77
+ )
78
+
79
+ # Zapis konfiguracji przez config_manager
80
+ config_dict = payload.dict()
81
+ save_guild_config(guild_id, config_dict)
82
+
83
+ logger.info(f"Zapisano now膮 konfiguracj臋 dla serwera Discord {guild_id}")
84
+ return {
85
+ "status": "success",
86
+ "message": f"Konfiguracja dla serwera {guild_id} zosta艂a zapisana.",
87
+ "config": config_dict
88
+ }
89
+
90
  @router.post(
91
  "/analyze",
92
  response_model=AnalysisResponse,
backend/app/config_manager.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ import logging
4
+ from typing import Dict, Any, Optional
5
+
6
+ logger = logging.getLogger(__name__)
7
+ CONFIG_FILE = "guild_configs.json"
8
+
9
+ def _load_all_configs() -> Dict[str, Dict[str, Any]]:
10
+ """Odczytuje konfiguracje wszystkich serwer贸w z pliku."""
11
+ if os.path.exists(CONFIG_FILE):
12
+ try:
13
+ with open(CONFIG_FILE, "r", encoding="utf-8") as f:
14
+ return json.load(f)
15
+ except Exception as e:
16
+ logger.error(f"B艂膮d podczas odczytu pliku konfiguracji: {e}")
17
+ return {}
18
+
19
+ def _save_all_configs(configs: Dict[str, Dict[str, Any]]):
20
+ """Zapisuje konfiguracje na dysk."""
21
+ try:
22
+ with open(CONFIG_FILE, "w", encoding="utf-8") as f:
23
+ json.dump(configs, f, indent=4, ensure_ascii=False)
24
+ except Exception as e:
25
+ logger.error(f"B艂膮d podczas zapisu pliku konfiguracji: {e}")
26
+
27
+ def save_guild_config(guild_id: str, config_data: Dict[str, Any]):
28
+ """Zapisuje kompletn膮 konfiguracj臋 dla danego serwera Discord."""
29
+ configs = _load_all_configs()
30
+ configs[guild_id] = config_data
31
+ _save_all_configs(configs)
32
+
33
+ def get_active_text_model(guild_id: str) -> Optional[str]:
34
+ """
35
+ Zwraca wybrany model dla serwera.
36
+ Zwraca None, je艣li konfiguracja nie istnieje lub model jest ustawiony na 'none'.
37
+ """
38
+ configs = _load_all_configs()
39
+ guild_config = configs.get(guild_id, {})
40
+ model = guild_config.get("active_text_model", "none")
41
+
42
+ if not model or model.lower() == "none":
43
+ return None
44
+ return model
backend/app/core/config.py CHANGED
@@ -29,7 +29,8 @@ class Settings:
29
  LOG_FILE: Optional[str] = os.getenv("LOG_FILE", None)
30
 
31
  AVAILABLE_MODELS = {
32
- "text": ["yaya36095/xlm-roberta-text-detector"],
 
33
  "image": ["capcheck/ai-image-detection"],
34
  }
35
 
 
29
  LOG_FILE: Optional[str] = os.getenv("LOG_FILE", None)
30
 
31
  AVAILABLE_MODELS = {
32
+ "text": ["yaya36095/xlm-roberta-text-detector",
33
+ "almanach/xlmr-chatgptdetect-noisy"],
34
  "image": ["capcheck/ai-image-detection"],
35
  }
36
 
backend/app/models/schemas.py CHANGED
@@ -1,5 +1,5 @@
1
  from pydantic import BaseModel, HttpUrl, Field
2
- from typing import Union, Literal, Optional
3
 
4
 
5
  class TextAnalysisRequest(BaseModel):
@@ -100,5 +100,17 @@ class HealthResponse(BaseModel):
100
  status: str = Field(..., description="Service status")
101
  service: str = Field(..., description="Service name")
102
  version: str = Field(..., description="Service version")
103
- available_models: dict = Field(..., description="Available detector models per content type")
104
- supported_types: list = Field(..., description="Supported content types")
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from pydantic import BaseModel, HttpUrl, Field
2
+ from typing import Union, Literal, Optional, Dict, List
3
 
4
 
5
  class TextAnalysisRequest(BaseModel):
 
100
  status: str = Field(..., description="Service status")
101
  service: str = Field(..., description="Service name")
102
  version: str = Field(..., description="Service version")
103
+ available_models: Dict[str, List[str]] = Field(
104
+ ..., description="Lista dost臋pnych modeli pogrupowana wed艂ug typ贸w"
105
+ )
106
+ supported_types: List[str] = Field(
107
+ ..., description="Obs艂ugiwane typy danych"
108
+ )
109
+ models_status: Dict[str, str] = Field(
110
+ ..., description="Status gotowo艣ci handler贸w dla poszczeg贸lnych typ贸w"
111
+ )
112
+
113
+ class GuildConfigSchema(BaseModel):
114
+ active_text_model: Optional[str] = "none"
115
+ # Tutaj mo偶esz doda膰 inne parametry, kt贸re bot zbiera w sesji setup (np. log_channel_id)
116
+ log_channel_id: Optional[str] = None
backend/app/services/text_analyzer.py CHANGED
@@ -1,45 +1,76 @@
1
  import logging
2
  import time
 
3
  from typing import Dict, Any
 
 
 
4
  from transformers import pipeline
 
 
5
 
6
  logger = logging.getLogger(__name__)
7
 
 
 
8
  _text_classifier = None
9
 
10
- def _load_model():
11
- global _text_classifier
12
- if _text_classifier is None:
13
- logger.info("Loading XLM-RoBERTa text detector model...")
14
- _text_classifier = pipeline(
15
- "text-classification",
16
- model="yaya36095/xlm-roberta-text-detector",
17
- device=-1
18
- )
19
- logger.info("Text detector model loaded successfully")
 
 
 
 
 
 
 
 
 
 
 
 
20
  return _text_classifier
21
 
22
- async def analyze_text(text: str) -> Dict[str, Any]:
23
  start_time = time.time()
24
 
25
- logger.info(f"Starting text analysis, length: {len(text)} chars")
 
26
 
27
- classifier = _load_model()
 
 
 
 
 
 
 
 
 
 
28
  result = classifier(text)
29
 
30
  label = result[0]["label"]
31
  score = result[0]["score"]
32
 
33
- is_deepfake = label.lower() == "fake"
34
  confidence = score
35
-
36
  analysis_time = time.time() - start_time
37
 
38
  response = {
39
  "is_deepfake": is_deepfake,
40
  "confidence": round(confidence, 3),
41
  "analysis_time": round(analysis_time, 3),
 
42
  }
43
 
44
- logger.info(f"Text analysis completed. Result: {response}")
45
- return response
 
1
  import logging
2
  import time
3
+ import gc
4
  from typing import Dict, Any
5
+
6
+ from app.config_manager import get_active_text_model
7
+ from app.utils.exceptions import SetupRequiredError
8
  from transformers import pipeline
9
+ # Importujesz helpery z Kroku 2:
10
+ # from config_manager import get_active_text_model
11
 
12
  logger = logging.getLogger(__name__)
13
 
14
+ # Przechowujemy nazw臋 aktualnie za艂adowanego modelu oraz sam obiekt klasyfikatora
15
+ _loaded_model_name = None
16
  _text_classifier = None
17
 
18
+ def _load_model(target_model_name: str):
19
+ global _text_classifier, _loaded_model_name
20
+
21
+ # Je艣li model w pami臋ci jest tym, kt贸rego potrzebujemy, po prostu go zwracamy
22
+ if _text_classifier is not None and _loaded_model_name == target_model_name:
23
+ return _text_classifier
24
+
25
+ logger.info(f"Wymagana zmiana modelu. Obecny w RAM: {_loaded_model_name}, Nowy: {target_model_name}")
26
+
27
+ # Zwalnianie pami臋ci po poprzednim modelu
28
+ _text_classifier = None
29
+ gc.collect()
30
+
31
+ logger.info(f"艁adowanie modelu text detector: {target_model_name}...")
32
+ _text_classifier = pipeline(
33
+ "text-classification",
34
+ model=target_model_name,
35
+ device=-1 # -1 oznacza CPU, je艣li masz GPU ustaw np. 0
36
+ )
37
+ _loaded_model_name = target_model_name
38
+ logger.info(f"Model {target_model_name} zosta艂 pomy艣lnie za艂adowany.")
39
+
40
  return _text_classifier
41
 
42
+ async def analyze_text(text: str, guild_id: str) -> Dict[str, Any]:
43
  start_time = time.time()
44
 
45
+ # Pobranie aktywnego modelu dla danej gildii
46
+ active_model = get_active_text_model(guild_id)
47
 
48
+ # BLOKADA: Je偶eli model to 'none' lub brak konfiguracji, natychmiast wyrzucamy b艂膮d
49
+ if not active_model:
50
+ logger.warning(f"Zablokowano zapytanie! Serwer {guild_id} nie ma skonfigurowanego modelu.")
51
+ raise SetupRequiredError(
52
+ f"Serwer o ID '{guild_id}' nie zosta艂 jeszcze skonfigurowany. "
53
+ "U偶yj komendy setup na Discordzie przed wykonaniem analizy."
54
+ )
55
+
56
+ logger.info(f"Rozpocz臋cie analizy tekstu dla serwera {guild_id} przy u偶yciu modelu: {active_model}")
57
+
58
+ classifier = _load_model(active_model)
59
  result = classifier(text)
60
 
61
  label = result[0]["label"]
62
  score = result[0]["score"]
63
 
64
+ is_deepfake = label.lower() in ["fake", "ai", "chatgpt", "label_1", "machine-generated"]
65
  confidence = score
 
66
  analysis_time = time.time() - start_time
67
 
68
  response = {
69
  "is_deepfake": is_deepfake,
70
  "confidence": round(confidence, 3),
71
  "analysis_time": round(analysis_time, 3),
72
+ "used_model": active_model,
73
  }
74
 
75
+ logger.info(f"Analiza zako艅czona sukcesem dla serwera {guild_id}.")
76
+ return response
backend/app/utils/exceptions.py CHANGED
@@ -51,3 +51,9 @@ class UnsupportedModelError(DeepfakeDetectionError):
51
  def __init__(self, model_name: str):
52
  message = f"Detector model '{model_name}' is not supported"
53
  super().__init__(message, 400)
 
 
 
 
 
 
 
51
  def __init__(self, model_name: str):
52
  message = f"Detector model '{model_name}' is not supported"
53
  super().__init__(message, 400)
54
+
55
+ class SetupRequiredError(Exception):
56
+ """Wyj膮tek zg艂aszany, gdy bot nie zosta艂 jeszcze skonfigurowany na danym serwerze."""
57
+
58
+ def __init__(self, message: str = "Setup required"):
59
+ super().__init__(message, 500)