Spaces:
Sleeping
Sleeping
Update main.py
Browse files
main.py
CHANGED
|
@@ -5,6 +5,7 @@ import math
|
|
| 5 |
import asyncio
|
| 6 |
from functools import lru_cache
|
| 7 |
from typing import Any, Dict, List
|
|
|
|
| 8 |
from fastapi.middleware.cors import CORSMiddleware
|
| 9 |
import uvicorn
|
| 10 |
from fastapi import FastAPI
|
|
@@ -103,39 +104,35 @@ LABEL_MAPPING = {
|
|
| 103 |
"intentional": ["intentional"]
|
| 104 |
}
|
| 105 |
|
| 106 |
-
ANALYZE_SYS_PROMPT = """You are a logic expert. Detect logical fallacies.
|
| 107 |
-
OUTPUT JSON ONLY.
|
| 108 |
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
2. "rationale": Explain WHY.
|
| 112 |
-
3. "confidence": 0.0 to 1.0.
|
| 113 |
|
| 114 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
"type": "ad hominem",
|
| 121 |
-
"confidence": 0.95,
|
| 122 |
-
"evidence_quotes": ["You are stupid"],
|
| 123 |
-
"rationale": "Direct attack on the person rather than the argument."
|
| 124 |
-
}}],
|
| 125 |
-
"overall_explanation": "Ad Hominem attack."
|
| 126 |
-
}}
|
| 127 |
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
|
|
|
|
|
|
|
|
|
| 139 |
|
| 140 |
JSON SCHEMA:
|
| 141 |
{{
|
|
@@ -150,40 +147,53 @@ JSON SCHEMA:
|
|
| 150 |
],
|
| 151 |
"overall_explanation": string
|
| 152 |
}}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
"""
|
| 154 |
|
| 155 |
-
REWRITE_SYS_PROMPT = """You are a
|
| 156 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
{{
|
| 158 |
"rewritten_text": string,
|
| 159 |
"why_this_fix": string
|
| 160 |
}}
|
| 161 |
-
"""
|
| 162 |
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
elif char == "}":
|
| 174 |
-
depth -= 1
|
| 175 |
-
if depth == 0:
|
| 176 |
-
potential_json = text[start:i + 1]
|
| 177 |
-
try:
|
| 178 |
-
json.loads(potential_json)
|
| 179 |
-
return potential_json
|
| 180 |
-
except Exception:
|
| 181 |
-
pass
|
| 182 |
-
|
| 183 |
-
end = text.rfind("}")
|
| 184 |
-
if start != -1 and end != -1:
|
| 185 |
-
return text[start:end + 1]
|
| 186 |
-
return text
|
| 187 |
|
| 188 |
def analyze_alternatives(start_index: int, top_logprobs_list: List[Dict[str, float]]) -> Dict[str, float]:
|
| 189 |
if start_index < 0 or start_index >= len(top_logprobs_list):
|
|
@@ -336,14 +346,15 @@ async def analyze(req: AnalyzeRequest):
|
|
| 336 |
logprobs = lp_data.get("token_logprobs", [])
|
| 337 |
top_logprobs = lp_data.get("top_logprobs", [])
|
| 338 |
|
| 339 |
-
|
| 340 |
result_json: Dict[str, Any] = {}
|
| 341 |
success = False
|
| 342 |
technical_confidence = 0.0
|
| 343 |
label_distribution: Dict[str, float] = {}
|
| 344 |
|
| 345 |
-
|
| 346 |
-
|
|
|
|
| 347 |
success = True
|
| 348 |
|
| 349 |
if result_json.get("has_fallacy") and result_json.get("fallacies"):
|
|
@@ -358,7 +369,7 @@ async def analyze(req: AnalyzeRequest):
|
|
| 358 |
fallacy["alternatives"] = label_distribution
|
| 359 |
|
| 360 |
declared = fallacy.get("confidence", 0.8)
|
| 361 |
-
fallacy["confidence"] = round((declared + spec_conf) / 2, 2)
|
| 362 |
|
| 363 |
if technical_confidence == 0.0:
|
| 364 |
technical_confidence = spec_conf
|
|
@@ -367,7 +378,7 @@ async def analyze(req: AnalyzeRequest):
|
|
| 367 |
info = extract_label_info("has_fallacy", tokens, logprobs, top_logprobs)
|
| 368 |
label_distribution = info["dist"]
|
| 369 |
|
| 370 |
-
|
| 371 |
result_json = {"error": "JSON Error", "raw": raw_text}
|
| 372 |
success = False
|
| 373 |
|
|
@@ -392,10 +403,13 @@ async def rewrite(req: RewriteRequest):
|
|
| 392 |
max_tokens=req.max_new_tokens,
|
| 393 |
temperature=0.7,
|
| 394 |
repeat_penalty=1.1,
|
| 395 |
-
stop=["</s>", "
|
| 396 |
)
|
| 397 |
try:
|
| 398 |
-
|
|
|
|
|
|
|
|
|
|
| 399 |
ok = True
|
| 400 |
except Exception:
|
| 401 |
res = {"raw": output["choices"][0]["text"]}
|
|
|
|
| 5 |
import asyncio
|
| 6 |
from functools import lru_cache
|
| 7 |
from typing import Any, Dict, List
|
| 8 |
+
from utils import extract_json_obj_robust, sanitize_analyze_output
|
| 9 |
from fastapi.middleware.cors import CORSMiddleware
|
| 10 |
import uvicorn
|
| 11 |
from fastapi import FastAPI
|
|
|
|
| 104 |
"intentional": ["intentional"]
|
| 105 |
}
|
| 106 |
|
|
|
|
|
|
|
| 107 |
|
| 108 |
+
ANALYZE_SYS_PROMPT = """You are a logic expert. Detect logical fallacies in the given text.
|
| 109 |
+
OUTPUT JSON ONLY. No markdown. No extra keys. No commentary outside JSON.
|
|
|
|
|
|
|
| 110 |
|
| 111 |
+
IMPORTANT:
|
| 112 |
+
- The text can contain MULTIPLE fallacies. Return ALL that apply.
|
| 113 |
+
- If there are NO fallacies, set "has_fallacy": false and "fallacies": [].
|
| 114 |
+
- "evidence_quotes" MUST be the SHORTEST exact span(s) from the input that justify the fallacy.
|
| 115 |
+
Do NOT quote the whole text. Prefer 1 short quote; at most 3 quotes.
|
| 116 |
|
| 117 |
+
LABELS:
|
| 118 |
+
Use ONLY these labels: {labels}
|
| 119 |
+
Do NOT invent labels.
|
| 120 |
+
Do NOT output "none" as a fallacy item.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
|
| 122 |
+
RATIONALE QUALITY (VERY IMPORTANT):
|
| 123 |
+
Each fallacy "rationale" MUST be directly tied to THIS input text, and MUST NOT be generic.
|
| 124 |
+
Structure each rationale like this (2–3 sentences max):
|
| 125 |
+
1) Restate the specific claim from the input in your own words AND anchor it to the exact quote(s).
|
| 126 |
+
2) Explain why that claim matches the fallacy, referencing what is missing or what is assumed.
|
| 127 |
+
3) If relevant, mention a concrete cue in the text.
|
| 128 |
+
|
| 129 |
+
OVERALL EXPLANATION (MUST reference the input):
|
| 130 |
+
- First: a quick recap of the detected fallacies (1 short sentence).
|
| 131 |
+
- Then: a general explanation of why these fallacies are risky IN THIS TEXT.
|
| 132 |
+
- If no fallacy: briefly explain why the reasoning is acceptable / what would be needed to call it fallacious.
|
| 133 |
+
|
| 134 |
+
CONFIDENCE:
|
| 135 |
+
"confidence" is between 0.0 and 1.0.
|
| 136 |
|
| 137 |
JSON SCHEMA:
|
| 138 |
{{
|
|
|
|
| 147 |
],
|
| 148 |
"overall_explanation": string
|
| 149 |
}}
|
| 150 |
+
|
| 151 |
+
EXAMPLES (style guide — copy this style):
|
| 152 |
+
|
| 153 |
+
Input: "If we allow remote work, productivity will collapse and the company will fail."
|
| 154 |
+
Output:
|
| 155 |
+
{{
|
| 156 |
+
"has_fallacy": true,
|
| 157 |
+
"fallacies": [{{
|
| 158 |
+
"type": "false causality",
|
| 159 |
+
"confidence": 0.86,
|
| 160 |
+
"evidence_quotes": ["If we allow remote work, productivity will collapse", "the company will fail"],
|
| 161 |
+
"rationale": "The input implies that allowing remote work will directly cause productivity to collapse and lead to company failure (quotes: 'productivity will collapse', 'the company will fail') without supporting evidence. It treats a speculative outcome as a guaranteed causal chain, jumping from a policy change to extreme consequences."
|
| 162 |
+
}}],
|
| 163 |
+
"overall_explanation": "Recap: false causality. Risk: the text presents a shaky cause-and-effect chain as certain, which can push decisions based on fear rather than evidence and ignore alternative explanations."
|
| 164 |
+
}}
|
| 165 |
"""
|
| 166 |
|
| 167 |
+
REWRITE_SYS_PROMPT = """You are a careful editor. Rewrite the text to REMOVE the logical fallacy while PRESERVING the original meaning as much as possible.
|
| 168 |
+
|
| 169 |
+
Context:
|
| 170 |
+
- Predicted fallacy type: {fallacy_type}
|
| 171 |
+
- Rationale: {rationale}
|
| 172 |
+
|
| 173 |
+
GOAL:
|
| 174 |
+
- Keep the same overall intent, but soften / qualify claims so the reasoning is no longer fallacious.
|
| 175 |
+
- Avoid absolute language ("always", "everyone", "no one") unless fully justified in the text.
|
| 176 |
+
- Replace overgeneralizations with reasonable qualifiers ("some", "often", "can", "in some cases").
|
| 177 |
+
- If the issue is causality, add uncertainty or evidence requirements ("may contribute", "could be related", "without evidence we can't conclude").
|
| 178 |
+
- If the issue is a false dilemma, add alternatives and nuance.
|
| 179 |
+
- If the issue is ad hominem / credibility attacks, remove personal attacks and focus on claims/evidence.
|
| 180 |
+
|
| 181 |
+
OUTPUT JSON ONLY (no markdown):
|
| 182 |
{{
|
| 183 |
"rewritten_text": string,
|
| 184 |
"why_this_fix": string
|
| 185 |
}}
|
|
|
|
| 186 |
|
| 187 |
+
The "why_this_fix" must be short (1-2 sentences) and explain what you changed to remove the fallacy.
|
| 188 |
+
|
| 189 |
+
EXAMPLE:
|
| 190 |
+
Input idea: "All blond women are pretty."
|
| 191 |
+
Output:
|
| 192 |
+
{{
|
| 193 |
+
"rewritten_text": "Some blond women can be very pretty, but attractiveness varies from person to person.",
|
| 194 |
+
"why_this_fix": "It removes the absolute generalization and replaces it with a qualified statement that doesn't stereotype an entire group."
|
| 195 |
+
}}
|
| 196 |
+
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 197 |
|
| 198 |
def analyze_alternatives(start_index: int, top_logprobs_list: List[Dict[str, float]]) -> Dict[str, float]:
|
| 199 |
if start_index < 0 or start_index >= len(top_logprobs_list):
|
|
|
|
| 346 |
logprobs = lp_data.get("token_logprobs", [])
|
| 347 |
top_logprobs = lp_data.get("top_logprobs", [])
|
| 348 |
|
| 349 |
+
parsed_obj = extract_json_obj_robust(raw_text)
|
| 350 |
result_json: Dict[str, Any] = {}
|
| 351 |
success = False
|
| 352 |
technical_confidence = 0.0
|
| 353 |
label_distribution: Dict[str, float] = {}
|
| 354 |
|
| 355 |
+
if parsed_obj is not None:
|
| 356 |
+
# Enforce schema + clean common template artifacts
|
| 357 |
+
result_json = sanitize_analyze_output(parsed_obj, req.text)
|
| 358 |
success = True
|
| 359 |
|
| 360 |
if result_json.get("has_fallacy") and result_json.get("fallacies"):
|
|
|
|
| 369 |
fallacy["alternatives"] = label_distribution
|
| 370 |
|
| 371 |
declared = fallacy.get("confidence", 0.8)
|
| 372 |
+
fallacy["confidence"] = round((float(declared) + float(spec_conf)) / 2, 2)
|
| 373 |
|
| 374 |
if technical_confidence == 0.0:
|
| 375 |
technical_confidence = spec_conf
|
|
|
|
| 378 |
info = extract_label_info("has_fallacy", tokens, logprobs, top_logprobs)
|
| 379 |
label_distribution = info["dist"]
|
| 380 |
|
| 381 |
+
else:
|
| 382 |
result_json = {"error": "JSON Error", "raw": raw_text}
|
| 383 |
success = False
|
| 384 |
|
|
|
|
| 403 |
max_tokens=req.max_new_tokens,
|
| 404 |
temperature=0.7,
|
| 405 |
repeat_penalty=1.1,
|
| 406 |
+
stop=["</s>", "```"],
|
| 407 |
)
|
| 408 |
try:
|
| 409 |
+
parsed = extract_json_obj_robust(output["choices"][0]["text"])
|
| 410 |
+
if parsed is None:
|
| 411 |
+
raise ValueError("json_parse_failed")
|
| 412 |
+
res = parsed
|
| 413 |
ok = True
|
| 414 |
except Exception:
|
| 415 |
res = {"raw": output["choices"][0]["text"]}
|