maxime-antoine-dev commited on
Commit
d9b52fd
·
verified ·
1 Parent(s): b1c3a2d

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +77 -63
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
- RULES:
110
- 1. Use ONLY these labels: {labels}
111
- 2. "rationale": Explain WHY.
112
- 3. "confidence": 0.0 to 1.0.
113
 
114
- EXAMPLES (Follow this logic):
 
 
 
 
115
 
116
- Input: "You are stupid, so your opinion is wrong."
117
- Output: {{
118
- "has_fallacy": true,
119
- "fallacies": [{{
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
- Input: "Think of the children! We must ban this immediately or they will suffer!"
129
- Output: {{
130
- "has_fallacy": true,
131
- "fallacies": [{{
132
- "type": "appeal to emotion",
133
- "confidence": 0.90,
134
- "evidence_quotes": ["Think of the children", "they will suffer"],
135
- "rationale": "Uses fear and pity to manipulate opinion without logical proof."
136
- }}],
137
- "overall_explanation": "Manipulative emotional appeal."
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 text editor. Rewrite to remove the fallacy.
156
- Output Format (JSON):
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  {{
158
  "rewritten_text": string,
159
  "why_this_fix": string
160
  }}
161
- """
162
 
163
- def clean_and_repair_json(text: str) -> str:
164
- text = text.replace("```json", "").replace("```", "").strip()
165
- start = text.find("{")
166
- if start == -1:
167
- return text
168
-
169
- depth = 0
170
- for i, char in enumerate(text[start:], start=start):
171
- if char == "{":
172
- depth += 1
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
- cleaned_text = clean_and_repair_json(raw_text)
340
  result_json: Dict[str, Any] = {}
341
  success = False
342
  technical_confidence = 0.0
343
  label_distribution: Dict[str, float] = {}
344
 
345
- try:
346
- result_json = json.loads(cleaned_text)
 
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
- except json.JSONDecodeError:
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
- res = json.loads(clean_and_repair_json(output["choices"][0]["text"]))
 
 
 
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"]}