.
- Never output JSON.
- Never use code fences.
- Never create Markdown tables.
- Use clean Markdown headings.
- Use bullet points.
- Use short readable paragraphs.
- Do not invent information.
- Use only information supported by the research paper.
"""
},
{
"role": "user",
"content": prompt
}
],
temperature=temperature,
max_tokens=max_tokens
)
result = response.choices[0].message.content
if not result:
return "No response generated by the model."
return clean_llm_output(result)
except Exception as e:
return f"Error: {str(e)}"
def clean_llm_output(text: str) -> str:
if not text:
return ""
import html
# Decode escaped HTML entities
text = html.unescape(text)
# Remove ALL HTML/XML tags
text = re.sub(
r"<[^>]+>",
"",
text,
flags=re.IGNORECASE
)
# Remove code fences
text = re.sub(
r"```(?:markdown|md|text)?",
"",
text,
flags=re.IGNORECASE
)
text = text.replace("```", "")
# Remove Markdown table separator lines
text = re.sub(
r"(?m)^\s*\|?[\s:-]+\|[\s|:-]*\|?\s*$",
"",
text
)
# Remove common reasoning prefixes
text = re.sub(
r"^(?:here(?:'s| is)\s+(?:a\s+)?thinking process:).*?\n",
"",
text,
flags=re.IGNORECASE | re.DOTALL
)
# Remove accidental "thinking process" blocks
text = re.sub(
r"(?is)(?:^|\n)(?:thinking process|analysis|reasoning)\s*:.*?(?=\n(?:##|\*\*\d+\.))",
"",
text
)
# Remove common reasoning/thinking prefixes
text = re.sub(
r"(?is)^(?:here(?:'s| is)\s+)?(?:a\s+)?(?:thinking process|analysis|reasoning)\s*:.*?(?=##\s*\d+\.|\*\*\d+\.)",
"",
text
)
# Remove standalone thinking headings
text = re.sub(
r"(?im)^\s*(thinking process|analysis|reasoning)\s*:?\s*$",
"",
text
)
# Fix escaped characters
text = text.replace("\\n", "\n")
text = text.replace("\\t", " ")
# Normalize spaces
text = re.sub(r"[ \t]+", " ", text)
# Maximum 2 consecutive newlines
text = re.sub(r"\n{3,}", "\n\n", text)
return text.strip()
# =========================================================
# CREATE PDF REPORT
# =========================================================
def create_pdf_report(summary, flowchart_img_path=None):
report_path = "research_report.pdf"
doc = SimpleDocTemplate(
report_path,
pagesize=letter,
rightMargin=54, leftMargin=54, topMargin=54, bottomMargin=54
)
styles = getSampleStyleSheet()
title_style = ParagraphStyle(
'DocTitle',
parent=styles['Title'],
fontName='Helvetica-Bold',
fontSize=24,
leading=28,
textColor=colors.HexColor('#1E293B'),
alignment=0,
spaceAfter=15
)
body_style = ParagraphStyle(
'DocBody',
parent=styles['BodyText'],
fontName='Helvetica',
fontSize=10.5,
leading=16,
textColor=colors.HexColor('#334155'),
spaceAfter=12
)
heading_style = ParagraphStyle(
'SectionHeading',
parent=styles['Heading2'],
fontName='Helvetica-Bold',
fontSize=14,
leading=18,
textColor=colors.HexColor('#0F172A'),
spaceBefore=14,
spaceAfter=8,
keepWithNext=True
)
subheading_style = ParagraphStyle(
'SubSectionHeading',
parent=styles['Heading3'],
fontName='Helvetica-Bold',
fontSize=12,
leading=16,
textColor=colors.HexColor('#334155'),
spaceBefore=10,
spaceAfter=6,
keepWithNext=True
)
story = []
story.append(Paragraph("Executive Research Analysis Report", title_style))
story.append(Spacer(1, 10))
safe_summary = clean_llm_output(summary)
for block in safe_summary.split("\n\n"):
block = block.strip()
if not block:
continue
# Main headings
if block.startswith("## "):
heading = block.replace("## ", "", 1).strip()
story.append(
Paragraph(
escape(heading),
heading_style
)
)
# Subheadings
elif block.startswith("### "):
subheading = block.replace("### ", "", 1).strip()
story.append(
Paragraph(
escape(subheading),
subheading_style
)
)
# Bullet points
elif any(line.strip().startswith("- ") for line in block.splitlines()):
for line in block.splitlines():
line = line.strip()
if not line:
continue
if line.startswith("- "):
bullet = escape(line[2:].strip())
story.append(
Paragraph(
"• " + bullet,
body_style
)
)
else:
story.append(
Paragraph(
escape(line),
body_style
)
)
# Normal paragraph
else:
safe_block = escape(block).replace("\n", " ")
story.append(
Paragraph(
safe_block,
body_style
)
)
story.append(Spacer(1, 6))
if flowchart_img_path and os.path.exists(flowchart_img_path):
img = ImageReader(flowchart_img_path)
orig_w, orig_h = img.getSize()
MAX_WIDTH = 450
MAX_HEIGHT = 550
scale = min(MAX_WIDTH / float(orig_w), MAX_HEIGHT / float(orig_h))
final_w = orig_w * scale
final_h = orig_h * scale
diagram_elements = [
Spacer(1, 15),
Paragraph("System Workflow & Logic Structural Diagram", heading_style),
Spacer(1, 15),
RLImage(flowchart_img_path, width=final_w, height=final_h)
]
story.append(KeepTogether(diagram_elements))
doc.build(story)
return report_path
# =========================================================
# ENDPOINTS
# =========================================================
@app.post("/analyze-paper")
async def analyze_paper(pdf_file: UploadFile = File(None), text: str = Form(None)):
global global_summary, global_flowchart_path
text_data = ""
try:
if pdf_file:
with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as temp_pdf:
temp_pdf.write(await pdf_file.read())
temp_path = temp_pdf.name
text_data = extract_text_from_pdf(temp_path)
elif text:
text_data = text
else:
return {"error": "No PDF or text data detected"}
chunks = create_chunks(text_data)
build_vector_store(chunks)
analysis_context = "\n\n".join(chunks[:25])
summary_prompt = f"""
Analyze the research paper below and create a professional
academic executive summary.
IMPORTANT OUTPUT RULES:
1. Return ONLY the final answer.
2. Do NOT show reasoning.
3. Do NOT show a thinking process.
4. Do NOT output HTML or XML.
5. Do NOT use
,
, , , , or .
6. Do NOT use JSON.
7. Do NOT use code fences.
8. Do NOT use Markdown tables.
9. Use normal Markdown.
10. Use headings, bullet points and short paragraphs.
11. Do not invent information.
12. Use only information supported by the paper.
Use EXACTLY this structure:
## 1. MAIN RESEARCH OBJECTIVE
Explain:
- The main objective of the study.
- The research problem.
- The scope of the study.
- Important geographical or industry context.
## 2. KEY METHODOLOGY & FINDINGS
### Methodology
Explain:
- Research methodology.
- Data/source type.
- Research approach.
### Key Findings
Provide 4-6 important findings as bullet points.
### Important Evidence
Provide important:
- statistics
- measurements
- market figures
- experimental results
Only include information actually supported by the paper.
### Key Challenges
List the major technical, social, regulatory,
environmental and practical challenges.
## 3. BUSINESS & REAL-WORLD IMPLICATIONS
### Business Implications
Explain how the findings affect:
- companies
- industries
- markets
- investors
- technology providers
### Real-World Implications
Explain effects on:
- users
- society
- infrastructure
- safety
- environment
- public policy
### Future Outlook
Explain the future direction discussed by the authors.
## Executive Takeaway
Provide a concise 3-5 sentence conclusion.
RESEARCH PAPER:
{analysis_context}
"""
summary = generate_llm_response(summary_prompt, temperature=0.15, max_tokens=1800)
global_summary = summary
flow_text = " ".join(chunks[:8])
flow_prompt = f"Extract exactly 4-5 core logical milestones from this research process sequence flow.\nRules:\n- One absolute short step name per line\n- No sequence numbers or explanation text\n- Max 4 words per line\n\nPaper Text:\n{flow_text}"
steps_text = generate_llm_response(flow_prompt, temperature=0.2, max_tokens=500)
steps = [line.strip() for line in steps_text.split("\n") if line.strip() and len(line.strip()) < 40][:5]
if len(steps) < 2:
steps = ["Document Ingestion", "Feature Extraction", "Neural Aggregation", "Validation Verification", "Output Compilation"]
# 3. Create clean Executive Corporate Stylized Flowchart (COMPACT VERSION)
dot = Digraph()
# Reduced ranksep to 0.25 to bring vertical steps much closer together
dot.attr(rankdir="TB", dpi="200", ranksep="0.25")
# Reduced fontsize to 13 and margin to "0.2,0.1" to make boxes significantly shorter
dot.attr('node', fontname='Helvetica', fontsize='13', shape='box', style='filled, rounded',
color='#3B82F6', fillcolor='#F0F9FF', fontcolor='#1E3A8A', penwidth='1.2', margin="0.2,0.1")
dot.attr('edge', fontname='Helvetica', fontsize='9', color='#64748B', penwidth='1.2', arrowsize='0.7')
for i, step in enumerate(steps):
dot.node(str(i), f" {step} ")
for i in range(len(steps) - 1):
dot.edge(str(i), str(i + 1))
filename = f"flowchart_{uuid.uuid4().hex}"
output_img = dot.render(filename, format="png", cleanup=True)
global_flowchart_path = output_img
create_pdf_report(summary, output_img)
return {
"summary": summary,
"flowchart_url": f"/flowchart/{os.path.basename(output_img)}",
"download_url": "/download-report",
"analyzer_steps": [
{"title": f"{i+1}. {step}", "desc": ""}
for i, step in enumerate(steps)
]
}
except Exception as e:
return {"error": str(e)}
@app.post("/chat-with-paper")
async def chat_with_paper(question: str = Form(...)):
try:
if global_index is None:
return {"error": "Please analyze a paper first"}
relevant_chunks = search_chunks(question)
context = "\n\n".join(relevant_chunks)
prompt = f"""
You are an academic research assistant.
Answer the user's question using ONLY the supplied research-paper context.
OUTPUT RULES:
- Give the direct answer first.
- Use clean Markdown only.
- Use short paragraphs.
- Use bullet points when useful.
- Use ## or ### headings only when necessary.
- Do not use Markdown tables.
- Do not use HTML.
- Do not use XML.
- Do not use JSON.
- Do not use code fences.
- Do not show reasoning.
- Do not show thinking.
- Do not mention internal analysis.
- Do not invent information.
- Do not repeat the question.
If the answer is not available in the supplied context, say:
"The provided paper context does not contain enough information to answer this."
RESEARCH PAPER CONTEXT:
{context}
USER QUESTION:
{question}
Return only the final answer.
"""
return {"question": question, "answer": generate_llm_response(prompt, temperature=0.2, max_tokens=900)}
except Exception as e:
return {"error": str(e)}
@app.get("/download-report")
async def download_report():
return FileResponse("research_report.pdf", media_type="application/pdf", filename="research_report.pdf")
@app.post("/generate-flowchart")
async def generate_flowchart():
try:
global global_chunks
if not global_chunks:
return {"error": "Analyze PDF first in the primary engine tab."}
text_data = " ".join(global_chunks[:10])
prompt = f"""
You are extracting the research workflow from an academic paper.
Return EXACTLY 5 lines.
Each line MUST follow this format:
TITLE | DESCRIPTION
Rules:
- Exactly 5 lines.
- One milestone per line.
- Title must contain 2-5 words.
- Description must contain 8-20 words.
- Do not number the lines.
- Do not use Markdown.
- Do not use tables.
- Do not use HTML.
- Do not use XML.
- Do not explain your reasoning.
- Do not add introductory or concluding text.
- Do not invent information.
Example:
Problem Definition | Identifies the main research problem and objectives addressed by the study.
Literature Review | Reviews previous research, theories, datasets, and industry evidence relevant to the study.
Methodology | Describes the methods, technologies, experiments, or analytical framework used by researchers.
Results Analysis | Examines the major findings, measurements, comparisons, and evidence reported in the research.
Conclusions | Summarizes the research contribution, limitations, practical implications, and future directions.
Research Paper:
{text_data}
"""
steps_text = generate_llm_response(prompt, temperature=0.2, max_tokens=1000)
lines = [line.strip() for line in steps_text.split("\n") if "|" in line]
extracted_steps = []
for idx, line in enumerate(lines[:5]):
parts = line.split("|", 1)
title = parts[0].strip()
# ✨ FIX: Clean any leading digits (e.g., '1. ', '2. ') injected by the LLM
title = re.sub(r'^\d+[\.\s\-–]+', '', title)
desc = parts[1].strip()
extracted_steps.append({
"title": f"{idx+1}. {title}",
"desc": desc
})
if len(extracted_steps) < 3:
extracted_steps = [
{"title": "1. Core Objective Alignment", "desc": "The primary problem statement and initial experimental hypotheses are established from document constraints."},
{"title": "2. Data Assembly & Preprocessing", "desc": "Raw source matrices, baseline metrics, or study populations are clean-filtered for processing."},
{"title": "3. Experimental Framework Execution", "desc": "The main technical methodology, core algorithms, or operational tests are systematically deployed."},
{"title": "4. Performance Metrics Validation", "desc": "Outputs are analyzed against rigorous control benchmarks to verify scientific validation accuracy."},
{"title": "5. Insight Synthesis & Conclusions", "desc": "Final qualitative and quantitative findings are compiled alongside real-world implementation scopes."}
]
return {"steps": extracted_steps}
except Exception as e:
return {"error": str(e)}
@app.get("/flowchart/{filename}")
async def get_flowchart(filename: str):
return FileResponse(filename, media_type="image/png")
@app.get("/")
async def root():
return {"message": "Research Paper RAG API Active"}