AsyncCoder-10 commited on
Commit
cd05673
·
verified ·
1 Parent(s): 21e992e

Upload 24 files

Browse files
app/__init__.py ADDED
File without changes
app/__pycache__/__init__.cpython-314.pyc ADDED
Binary file (150 Bytes). View file
 
app/__pycache__/main.cpython-314.pyc ADDED
Binary file (990 Bytes). View file
 
app/api/__init__.py ADDED
File without changes
app/api/__pycache__/__init__.cpython-314.pyc ADDED
Binary file (154 Bytes). View file
 
app/api/v1/__init__.py ADDED
File without changes
app/api/v1/__pycache__/__init__.cpython-314.pyc ADDED
Binary file (157 Bytes). View file
 
app/api/v1/endpoints/__init__.py ADDED
File without changes
app/api/v1/endpoints/__pycache__/__init__.cpython-314.pyc ADDED
Binary file (167 Bytes). View file
 
app/api/v1/endpoints/__pycache__/moderation.cpython-314.pyc ADDED
Binary file (1.3 kB). View file
 
app/api/v1/endpoints/moderation.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, HTTPException
2
+ from app.schemas.moderation import ModerationRequest, ModerationResponse
3
+ from app.services.moderation_service import moderation_service
4
+
5
+ router = APIRouter()
6
+
7
+
8
+ @router.post("/moderate", response_model=ModerationResponse)
9
+ async def moderate_text(request: ModerationRequest):
10
+ """
11
+ Check a piece of text for toxicity. If flagged, returns 8 AI-generated safe alternatives.
12
+ """
13
+ try:
14
+ result = moderation_service.get_moderation_scores(request.text)
15
+ return ModerationResponse(**result)
16
+ except RuntimeError as e:
17
+ raise HTTPException(status_code=500, detail=str(e))
app/core/__init__.py ADDED
File without changes
app/core/__pycache__/__init__.cpython-314.pyc ADDED
Binary file (155 Bytes). View file
 
app/core/__pycache__/config.cpython-314.pyc ADDED
Binary file (1.58 kB). View file
 
app/core/config.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic_settings import BaseSettings
2
+ from functools import lru_cache
3
+
4
+
5
+ class Settings(BaseSettings):
6
+ openai_api_key: str # Keep for potential LLM rewrites
7
+ groq_api_key: str # Keep for potential LLM rewrites
8
+ moderation_threshold: float = 0.5
9
+ # Use a Hugging Face model ID
10
+ hf_moderation_model: str = "textdetox/bert-multilingual-toxicity-classifier"
11
+ chat_model: str = "openai/gpt-oss-20b"
12
+
13
+ class Config:
14
+ env_file = ".env"
15
+
16
+
17
+ @lru_cache()
18
+ def get_settings() -> Settings:
19
+ return Settings()
app/main.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from app.api.v1.endpoints.moderation import router as moderation_router
4
+
5
+ app = FastAPI(
6
+ title="Dynamic Moderation API",
7
+ description="Moderates text and generates dynamic safe alternatives using AI.",
8
+ version="1.0.0",
9
+ )
10
+
11
+ app.add_middleware(
12
+ CORSMiddleware,
13
+ allow_origins=["*"], # Change to your frontend URL in production
14
+ allow_credentials=True,
15
+ allow_methods=["*"],
16
+ allow_headers=["*"],
17
+ )
18
+
19
+ app.include_router(moderation_router, prefix="/api/v1")
20
+
21
+
22
+ @app.get("/health")
23
+ def health_check():
24
+ return {"status": "ok"}
app/schemas/__init__.py ADDED
File without changes
app/schemas/__pycache__/__init__.cpython-314.pyc ADDED
Binary file (158 Bytes). View file
 
app/schemas/__pycache__/moderation.cpython-314.pyc ADDED
Binary file (1.81 kB). View file
 
app/schemas/moderation.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel, Field
2
+ from typing import Dict, List
3
+
4
+
5
+ class ModerationRequest(BaseModel):
6
+ text: str = Field(..., min_length=1, description="The text to be moderated.")
7
+
8
+
9
+ class ModerationResponse(BaseModel):
10
+ is_flagged: bool
11
+ model_used: str
12
+ top_category: str
13
+ identified_meaning: str = Field(
14
+ default="", description="The core intent or meaning of the original text."
15
+ )
16
+ suggested_alternatives: List[str] = Field(
17
+ default_factory=list, description="7-8 dynamically reframed alternatives."
18
+ )
19
+ raw_scores: Dict[str, float]
app/services/__init__.py ADDED
File without changes
app/services/__pycache__/__init__.cpython-314.pyc ADDED
Binary file (159 Bytes). View file
 
app/services/__pycache__/moderation_service.cpython-314.pyc ADDED
Binary file (6.21 kB). View file
 
app/services/moderation_service.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ from openai import OpenAI
3
+ from app.core.config import get_settings
4
+ from typing import Dict, Any, List, Tuple
5
+ from transformers import pipeline
6
+
7
+
8
+ class ModerationService:
9
+ def __init__(self):
10
+ settings = get_settings()
11
+
12
+ # Initialize Hugging Face moderation pipeline (or OpenAI moderation)
13
+ self.hf_moderator = pipeline(
14
+ "text-classification", model=settings.hf_moderation_model
15
+ )
16
+
17
+ # Groq client for dynamic rewrites
18
+ self.groq_client = OpenAI(
19
+ base_url="https://api.groq.com/openai/v1", api_key=settings.groq_api_key
20
+ )
21
+
22
+ self.hf_model_name = settings.hf_moderation_model
23
+ self.chat_model = settings.chat_model
24
+ self.threshold = settings.moderation_threshold
25
+
26
+ def get_moderation_scores(self, text: str) -> Dict[str, Any]:
27
+ try:
28
+ # 1. Moderate
29
+ hf_result = self.hf_moderator(text)[0]
30
+ label = hf_result["label"]
31
+ score = float(hf_result["score"])
32
+
33
+ is_flagged = (
34
+ label == "LABEL_1" or label.lower() == "toxic"
35
+ ) and score >= self.threshold
36
+ top_category = "toxic" if is_flagged else "safe"
37
+
38
+ # 2. Generate dynamic meaning and alternatives
39
+ identified_meaning = ""
40
+ alternatives = []
41
+
42
+ if is_flagged:
43
+ identified_meaning, alternatives = self._analyze_and_reframe(
44
+ text, top_category
45
+ )
46
+
47
+ return {
48
+ "is_flagged": is_flagged,
49
+ "model_used": self.hf_model_name,
50
+ "top_category": top_category,
51
+ "identified_meaning": identified_meaning,
52
+ "suggested_alternatives": alternatives,
53
+ "raw_scores": {label: score},
54
+ }
55
+ except Exception as e:
56
+ raise RuntimeError(f"Moderation service failed: {str(e)}")
57
+
58
+ def _analyze_and_reframe(
59
+ self, original_text: str, category: str
60
+ ) -> Tuple[str, List[str]]:
61
+ prompt = f"""
62
+ The following text was flagged for toxicity:
63
+ ORIGINAL TEXT: "{original_text}"
64
+
65
+ Your task:
66
+ 1. Identify the core underlying meaning or emotion of this text. (e.g., "The user is frustrated because a deadline was missed.")
67
+ 2. Reframe this exact meaning into exactly 8 safe, polite, and constructive alternatives.
68
+
69
+ You MUST respond with a JSON object containing exactly two keys: "identified_meaning" (a string) and "alternatives" (an array of 8 strings).
70
+
71
+ Example:
72
+ {{
73
+ "identified_meaning": "The user is frustrated that their colleague is being slow.",
74
+ "alternatives": [
75
+ "I feel like we are moving a bit slowly on this.",
76
+ "Could we pick up the pace a little?",
77
+ "I am concerned about the timeline.",
78
+ "I would appreciate it if we could speed this up.",
79
+ "I think we need to be more efficient.",
80
+ "Let's try to get this done faster.",
81
+ "I'm feeling a bit impatient with the progress.",
82
+ "Can we focus and get this finished?"
83
+ ]
84
+ }}
85
+ """
86
+
87
+ try:
88
+ response = self.groq_client.chat.completions.create(
89
+ model=self.chat_model,
90
+ messages=[
91
+ {
92
+ "role": "system",
93
+ "content": "You are a helpful assistant that analyzes toxic text and outputs valid JSON.",
94
+ },
95
+ {"role": "user", "content": prompt},
96
+ ],
97
+ temperature=0.7,
98
+ response_format={"type": "json_object"},
99
+ )
100
+
101
+ content = response.choices[0].message.content
102
+ parsed_json = json.loads(content)
103
+
104
+ meaning = parsed_json.get(
105
+ "identified_meaning", "Could not identify meaning."
106
+ )
107
+
108
+ # Robustly find the list of alternatives
109
+ alts = []
110
+ for key, value in parsed_json.items():
111
+ if isinstance(value, list) and len(value) > 0:
112
+ alts = value
113
+ break
114
+
115
+ return meaning, alts
116
+
117
+ except Exception as e:
118
+ print(f"🚨 GROQ FAILED: {str(e)}")
119
+ return (
120
+ "Error analyzing meaning.",
121
+ [
122
+ "Please ensure your message is respectful.",
123
+ "I disagree with you, but let's keep it civil.",
124
+ "Can we discuss this without insults?",
125
+ "I'd prefer to focus on the facts.",
126
+ "Let's take a moment to cool down.",
127
+ "I think we should agree to disagree.",
128
+ "Please rephrase your message politely.",
129
+ "Let's try to find common ground.",
130
+ ],
131
+ )
132
+
133
+
134
+ moderation_service = ModerationService()