Skip to content

Commit 06df29c

Browse files
committed
feat(chat): include article context in AI Discussion and merge with RAG context
1 parent 1b69fb1 commit 06df29c

4 files changed

Lines changed: 77 additions & 24 deletions

File tree

backend/app/modules/chat/llm_processing.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,23 @@ def build_context(docs):
3939
return "\n".join(
4040
f"{m['metadata'].get('explanation') or m['metadata'].get('reasoning', '')}"
4141
for m in docs
42-
)
42+
).strip()
43+
44+
45+
def ask_llm(question, docs, article_context=""):
46+
rag_context = build_context(docs)
47+
article_context = (article_context or "").strip()
48+
49+
if rag_context and article_context:
50+
context = f"Article:\n{article_context}\n\nRetrieved Insights:\n{rag_context}"
51+
else:
52+
context = rag_context or article_context
4353

54+
if not context:
55+
return (
56+
"I don't have article context yet. Please analyze an article first and then ask me again."
57+
)
4458

45-
def ask_llm(question, docs):
46-
context = build_context(docs)
4759
logger.debug(f"Generated context for LLM:\n{context}")
4860
prompt = f"""You are an assistant that answers based on context.
4961

backend/app/routes/routes.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"""
3131

3232

33-
from fastapi import APIRouter
33+
from fastapi import APIRouter, HTTPException
3434
from pydantic import BaseModel
3535
from app.modules.pipeline import run_scraper_pipeline
3636
from app.modules.pipeline import run_langgraph_workflow
@@ -52,6 +52,7 @@ class URlRequest(BaseModel):
5252

5353
class ChatQuery(BaseModel):
5454
message: str
55+
article_context: str | None = None
5556

5657

5758
@router.get("/")
@@ -77,9 +78,20 @@ async def run_pipelines(request: URlRequest):
7778

7879
@router.post("/chat")
7980
async def answer_query(request: ChatQuery):
80-
query = request.message
81-
results = search_pinecone(query)
82-
answer = ask_llm(query, results)
83-
logger.info(f"Chat answer generated: {answer}")
84-
85-
return {"answer": answer}
81+
try:
82+
query = request.message.strip()
83+
if not query:
84+
raise HTTPException(status_code=400, detail="Message cannot be empty.")
85+
86+
article_context = (request.article_context or "").strip()
87+
88+
results = search_pinecone(query)
89+
answer = ask_llm(query, results, article_context)
90+
logger.info("Chat answer generated successfully.")
91+
92+
return {"answer": answer}
93+
except HTTPException:
94+
raise
95+
except Exception as e:
96+
logger.exception(f"Chat request failed: {e}")
97+
raise HTTPException(status_code=500, detail="Failed to generate chat response.")

backend/package-lock.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/app/analyze/results/page.tsx

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { Badge } from "@/components/ui/badge";
1919
import BiasMeter from "@/components/bias-meter";
2020
import axios from "axios";
2121

22-
const backend_url = process.env.NEXT_PUBLIC_API_URL;
22+
const backendUrl = (process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:8000").trim();
2323

2424
/**
2525
* Renders the article analysis page with summary, perspectives, fact checks, bias meter, AI chat, and sources.
@@ -32,6 +32,7 @@ export default function AnalyzePage() {
3232
const [activeTab, setActiveTab] = useState("summary");
3333
const [message, setMessage] = useState("");
3434
const [isLoading, setIsLoading] = useState(true);
35+
const [isChatLoading, setIsChatLoading] = useState(false);
3536
const [messages, setMessages] = useState<{ role: string; content: string }[]>(
3637
[
3738
{
@@ -80,20 +81,42 @@ export default function AnalyzePage() {
8081

8182
async function handleSendMessage(e: React.FormEvent) {
8283
e.preventDefault();
83-
if (!message.trim()) return;
84-
const newMessages = [...messages, { role: "user", content: message }];
84+
const userMessage = message.trim();
85+
if (!userMessage || isChatLoading) return;
86+
87+
const newMessages = [...messages, { role: "user", content: userMessage }];
8588
setMessages(newMessages);
8689
setMessage("");
90+
setIsChatLoading(true);
8791

88-
const res = await axios.post(`${backend_url}/api/chat`, {
89-
message: message,
90-
});
91-
const data = res.data;
92-
93-
console.log(data);
92+
try {
93+
const articleContext = analysisData?.cleaned_text?.trim() ?? "";
94+
const res = await axios.post(
95+
`${backendUrl}/api/chat`,
96+
{
97+
message: userMessage,
98+
article_context: articleContext,
99+
},
100+
{ timeout: 45000 }
101+
);
102+
const answer = res.data?.answer ?? "I could not generate an answer right now.";
94103

95-
// 🔹 Step 2: Append LLM’s response
96-
setMessages([...newMessages, { role: "assistant", content: data.answer }]);
104+
setMessages([...newMessages, { role: "assistant", content: answer }]);
105+
} catch (error: any) {
106+
const fallback =
107+
error?.response?.data?.detail ||
108+
error?.message ||
109+
"Chat request failed. Please try again.";
110+
setMessages([
111+
...newMessages,
112+
{
113+
role: "assistant",
114+
content: `Sorry, I couldn't fetch a reply: ${fallback}`,
115+
},
116+
]);
117+
} finally {
118+
setIsChatLoading(false);
119+
}
97120
}
98121
if (isLoading) {
99122
return (
@@ -187,10 +210,10 @@ export default function AnalyzePage() {
187210
<Badge
188211
variant={
189212
fact.verdict === "True"
190-
? "success"
213+
? "secondary"
191214
: fact.verdict === "False"
192215
? "destructive"
193-
: "warning"
216+
: "outline"
194217
}
195218
>
196219
{fact.verdict}
@@ -255,7 +278,7 @@ export default function AnalyzePage() {
255278
value={message}
256279
onChange={(e) => setMessage(e.target.value)}
257280
/>
258-
<Button type="submit" disabled={!message.trim()}>
281+
<Button type="submit" disabled={!message.trim() || isChatLoading}>
259282
<Send />
260283
</Button>
261284
</form>

0 commit comments

Comments
 (0)