Lumiin0us commited on
Commit
027bfe8
·
unverified ·
0 Parent(s):

AutoScholar

Browse files
agent/__init__.py ADDED
File without changes
agent/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (161 Bytes). View file
 
agent/__pycache__/graph.cpython-310.pyc ADDED
Binary file (922 Bytes). View file
 
agent/__pycache__/state.cpython-310.pyc ADDED
Binary file (597 Bytes). View file
 
agent/graph.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langgraph.graph import StateGraph, END
2
+ from agent.state import ResearchState
3
+ from agent.nodes.reader import reader_node
4
+ from agent.nodes.search import search_node
5
+ from agent.nodes.planner import planner_node
6
+ from agent.nodes.synthesizer import synthesizer_node
7
+ from agent.nodes.reflection import reflection_node, should_continue
8
+
9
+ # Create the graph
10
+ graph = StateGraph(ResearchState)
11
+
12
+ # Add all nodes
13
+ graph.add_node("planner", planner_node)
14
+ graph.add_node("search", search_node)
15
+ graph.add_node("reader", reader_node)
16
+ graph.add_node("synthesizer", synthesizer_node)
17
+ graph.add_node("reflection", reflection_node)
18
+
19
+ # Add edges (flow between nodes)
20
+ graph.set_entry_point("planner")
21
+ graph.add_edge("planner", "search")
22
+ graph.add_edge("search", "reader")
23
+ graph.add_edge("reader", "synthesizer")
24
+ graph.add_edge("synthesizer", "reflection")
25
+
26
+ # Conditional edge - reflection either ends or loops back
27
+ graph.add_conditional_edges(
28
+ "reflection",
29
+ should_continue,
30
+ {
31
+ "search": "search", # loop back
32
+ "END": END # finish
33
+ }
34
+ )
35
+
36
+ app = graph.compile()
agent/nodes/__init__.py ADDED
File without changes
agent/nodes/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (167 Bytes). View file
 
agent/nodes/__pycache__/planner.cpython-310.pyc ADDED
Binary file (1.11 kB). View file
 
agent/nodes/__pycache__/reader.cpython-310.pyc ADDED
Binary file (836 Bytes). View file
 
agent/nodes/__pycache__/reflection.cpython-310.pyc ADDED
Binary file (1.11 kB). View file
 
agent/nodes/__pycache__/search.cpython-310.pyc ADDED
Binary file (625 Bytes). View file
 
agent/nodes/__pycache__/synthesizer.cpython-310.pyc ADDED
Binary file (879 Bytes). View file
 
agent/nodes/planner.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from agent.state import ResearchState
2
+ from langchain_groq import ChatGroq
3
+
4
+ llm = ChatGroq(model="llama-3.3-70b-versatile", api_key="")
5
+
6
+ def parse_questions(text: str) -> list:
7
+ lines = text.strip().split("\n")
8
+ questions = []
9
+
10
+ for line in lines:
11
+ line = line.strip()
12
+ if line: # skip empty lines
13
+ cleaned = line.lstrip("0123456789.-) ").strip()
14
+ if cleaned:
15
+ questions.append(cleaned)
16
+
17
+ return questions
18
+
19
+ def planner_node(state: ResearchState):
20
+ prompt = f"""
21
+ You are a research planner. Break this topic into
22
+ 3-4 specific search queries:
23
+
24
+ Topic: {state['topic']}
25
+
26
+ Return only a numbered list of search queries.
27
+ """
28
+ response = llm.invoke(prompt)
29
+ # parse response into a list
30
+ sub_questions = parse_questions(response.content)
31
+
32
+ return {"sub_questions": sub_questions}
agent/nodes/reader.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import httpx
2
+ from bs4 import BeautifulSoup
3
+ from agent.state import ResearchState
4
+
5
+ def reader_node(state: ResearchState):
6
+ scraped = []
7
+
8
+ for result in state['search_results'][:3]: # top 3 only
9
+ try:
10
+ response = httpx.get(result['url'], timeout=5)
11
+ soup = BeautifulSoup(response.text, 'html.parser')
12
+ text = ' '.join(p.text for p in soup.find_all('p'))
13
+ scraped.append(text[:2000])
14
+ except:
15
+ continue
16
+
17
+ return {"scraped_content": scraped}
agent/nodes/reflection.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from agent.state import ResearchState
2
+ from langchain_groq import ChatGroq
3
+
4
+ llm = ChatGroq(model="llama-3.3-70b-versatile", api_key="")
5
+
6
+ def reflection_node(state: ResearchState):
7
+ prompt = f"""
8
+ Original question: {state['topic']}
9
+
10
+ Generated report: {state['report']}
11
+
12
+ Does this report fully and accurately answer the
13
+ original question? Reply with only YES or NO,
14
+ then one sentence explanation.
15
+ """
16
+ response = llm.invoke(prompt)
17
+ passed = response.content.strip().upper().startswith("YES")
18
+
19
+ return {
20
+ "reflection_passed": passed,
21
+ "loop_count": state['loop_count'] + 1
22
+ }
23
+
24
+ # decides the next node based on state
25
+ def should_continue(state: ResearchState):
26
+ if state['reflection_passed'] or state['loop_count'] >= 2:
27
+ return "END"
28
+ else:
29
+ return "search" # loop back and search again
agent/nodes/search.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from tavily import TavilyClient
2
+ from agent.state import ResearchState
3
+
4
+ tavily = TavilyClient(api_key="")
5
+
6
+ def search_node(state: ResearchState):
7
+ all_results = []
8
+
9
+ for question in state['sub_questions']:
10
+ results = tavily.search(question, max_results=3)
11
+ all_results.extend(results['results'])
12
+
13
+ return {"search_results": all_results}
agent/nodes/synthesizer.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_groq import ChatGroq
2
+ from agent.state import ResearchState
3
+
4
+ llm = ChatGroq(model="llama-3.3-70b-versatile", api_key="")
5
+
6
+ def synthesizer_node(state: ResearchState):
7
+ content = "\n\n".join(state['scraped_content'])
8
+
9
+ prompt = f"""
10
+ You are a research analyst. Using the content below,
11
+ write a comprehensive structured report on:
12
+
13
+ Topic: {state['topic']}
14
+
15
+ Content: {content}
16
+
17
+ Format: Introduction, Key Findings, Conclusion, Sources
18
+ """
19
+ response = llm.invoke(prompt)
20
+
21
+ return {"report": response.content}
agent/state.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import TypedDict, List
2
+
3
+ class ResearchState(TypedDict):
4
+ topic: str # user's original question
5
+ sub_questions: List[str] # broken down by planner node
6
+ search_results: List[dict] # raw results from Tavily
7
+ scraped_content: List[str] # full page text
8
+ report: str # final synthesized report
9
+ reflection_passed: bool # did reflection approve?
10
+ loop_count: int # prevent infinite loops
streamlit.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import os
3
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
4
+ import streamlit as st
5
+ from agent.graph import app
6
+
7
+ st.title("AI Research Assistant")
8
+ topic = st.text_input("Enter research topic:")
9
+
10
+ if st.button("Research"):
11
+ with st.spinner("Agent is researching..."):
12
+ result = app.invoke({
13
+ "topic": topic,
14
+ "sub_questions": [],
15
+ "search_results": [],
16
+ "scraped_content": [],
17
+ "report": "",
18
+ "reflection_passed": False,
19
+ "loop_count": 0
20
+ })
21
+ st.markdown(result['report'])