Ani14 commited on
Commit
f7fc82d
·
verified ·
1 Parent(s): 8271c6c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +66 -49
app.py CHANGED
@@ -5,11 +5,10 @@ import feedparser
5
  from dotenv import load_dotenv
6
  from duckduckgo_search import DDGS
7
 
8
- # Load API key from .env
9
  load_dotenv()
10
  OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY")
11
 
12
- # --- LLM Function ---
13
  def call_llm(messages, model="deepseek/deepseek-chat-v3-0324:free", max_tokens=2048, temperature=0.7):
14
  url = "https://openrouter.ai/api/v1/chat/completions"
15
  headers = {
@@ -29,13 +28,12 @@ def call_llm(messages, model="deepseek/deepseek-chat-v3-0324:free", max_tokens=2
29
  except Exception as e:
30
  raise RuntimeError(f"Failed to connect or parse response: {e}")
31
  if response.status_code != 200:
32
- error = result.get("error", {}).get("message", response.text)
33
- raise RuntimeError(f"OpenRouter API Error: {error}")
34
  if "choices" not in result:
35
  raise RuntimeError(f"Invalid response: {result}")
36
  return result["choices"][0]["message"]["content"]
37
 
38
- # --- Source Fetching Functions ---
39
  def get_arxiv_papers(query, max_results=3):
40
  from urllib.parse import quote_plus
41
  url = f"http://export.arxiv.org/api/query?search_query=all:{quote_plus(query)}&start=0&max_results={max_results}"
@@ -50,12 +48,11 @@ def get_semantic_scholar_papers(query, max_results=3):
50
  url = "https://api.semanticscholar.org/graph/v1/paper/search"
51
  params = {"query": query, "limit": max_results, "fields": "title,abstract,url"}
52
  response = requests.get(url, params=params)
53
- papers = response.json().get("data", [])
54
  return [{
55
  "title": p.get("title", ""),
56
  "summary": p.get("abstract", "No abstract").strip(),
57
  "url": p.get("url", "")
58
- } for p in papers]
59
 
60
  def search_duckduckgo(query, max_results=3):
61
  with DDGS() as ddgs:
@@ -65,76 +62,96 @@ def search_duckduckgo(query, max_results=3):
65
  "url": r["href"]
66
  } for r in ddgs.text(query, max_results=max_results)]
67
 
68
- def get_image_url(query):
69
  with DDGS() as ddgs:
70
- for r in ddgs.images(query, max_results=1):
71
- return r["image"]
72
- return None
73
 
74
- # --- Autonomous Agent ---
75
  def autonomous_research_agent(topic):
76
  arxiv = get_arxiv_papers(topic)
77
  scholar = get_semantic_scholar_papers(topic)
78
  web = search_duckduckgo(topic)
79
- image = get_image_url(topic)
80
 
81
- prompt = f"# Research Topic: {topic}\n\n"
 
 
 
 
82
 
83
- if image:
84
- prompt += f"![Relevant Image]({image})\n\n"
85
-
86
- prompt += "## ArXiv:\n"
87
  for p in arxiv:
88
- prompt += f"- [{p['title']}]({p['url']})\n> {p['summary'][:300]}...\n\n"
89
 
90
- prompt += "## Semantic Scholar:\n"
91
  for p in scholar:
92
- prompt += f"- [{p['title']}]({p['url']})\n> {p['summary'][:300]}...\n\n"
93
 
94
- prompt += "## Web Insights:\n"
95
  for w in web:
96
- prompt += f"- [{w['title']}]({w['url']})\n> {w['snippet']}\n\n"
 
 
 
 
 
 
 
 
 
 
 
97
 
98
- prompt += (
99
- "Now, based on the above sources:\n"
100
- "1. Identify overlapping research themes\n"
101
- "2. Highlight a gap or opportunity\n"
102
- "3. Propose a novel research direction\n"
103
- "4. Write a full academic-style narrative in markdown (no section labels)\n"
104
- )
105
 
106
- return call_llm([{"role": "user", "content": prompt}], max_tokens=3000)
 
 
 
 
 
107
 
108
- # --- Streamlit App ---
109
- st.set_page_config("Autonomous Research Agent", layout="wide")
 
 
 
 
 
 
 
 
 
 
 
110
  st.title("🤖 Autonomous AI Research Assistant")
111
 
112
  if "chat_history" not in st.session_state:
113
  st.session_state.chat_history = []
114
 
115
  topic = st.text_input("Enter a research topic:")
116
- if st.button("Run Agent"):
117
  with st.spinner("Gathering sources & thinking..."):
118
  try:
119
- response = autonomous_research_agent(topic)
120
  st.session_state.chat_history.append({"role": "user", "content": topic})
121
- st.session_state.chat_history.append({"role": "assistant", "content": response})
122
- st.markdown(response)
123
  except Exception as e:
124
  st.error(f"Failed: {e}")
125
 
126
- # --- Follow-up Chat ---
127
  st.divider()
128
- st.subheader("💬 Follow-up Questions")
129
- followup = st.text_input("Ask something about the research:")
130
  if st.button("Ask"):
131
  if followup:
132
- chat = st.session_state.chat_history + [{"role": "user", "content": followup}]
133
- with st.spinner("Answering..."):
134
- try:
135
- answer = call_llm(chat, max_tokens=1500)
136
- st.session_state.chat_history.append({"role": "user", "content": followup})
137
- st.session_state.chat_history.append({"role": "assistant", "content": answer})
138
- st.markdown(answer)
139
- except Exception as e:
140
- st.error(f"Follow-up error: {e}")
 
5
  from dotenv import load_dotenv
6
  from duckduckgo_search import DDGS
7
 
 
8
  load_dotenv()
9
  OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY")
10
 
11
+ # --- Call OpenRouter LLM ---
12
  def call_llm(messages, model="deepseek/deepseek-chat-v3-0324:free", max_tokens=2048, temperature=0.7):
13
  url = "https://openrouter.ai/api/v1/chat/completions"
14
  headers = {
 
28
  except Exception as e:
29
  raise RuntimeError(f"Failed to connect or parse response: {e}")
30
  if response.status_code != 200:
31
+ raise RuntimeError(result.get("error", {}).get("message", "LLM API error"))
 
32
  if "choices" not in result:
33
  raise RuntimeError(f"Invalid response: {result}")
34
  return result["choices"][0]["message"]["content"]
35
 
36
+ # --- Search Utilities ---
37
  def get_arxiv_papers(query, max_results=3):
38
  from urllib.parse import quote_plus
39
  url = f"http://export.arxiv.org/api/query?search_query=all:{quote_plus(query)}&start=0&max_results={max_results}"
 
48
  url = "https://api.semanticscholar.org/graph/v1/paper/search"
49
  params = {"query": query, "limit": max_results, "fields": "title,abstract,url"}
50
  response = requests.get(url, params=params)
 
51
  return [{
52
  "title": p.get("title", ""),
53
  "summary": p.get("abstract", "No abstract").strip(),
54
  "url": p.get("url", "")
55
+ } for p in response.json().get("data", [])]
56
 
57
  def search_duckduckgo(query, max_results=3):
58
  with DDGS() as ddgs:
 
62
  "url": r["href"]
63
  } for r in ddgs.text(query, max_results=max_results)]
64
 
65
+ def get_image_urls(query, max_images=3):
66
  with DDGS() as ddgs:
67
+ return [img["image"] for img in ddgs.images(query, max_results=max_images)]
 
 
68
 
69
+ # --- Autonomous Research Agent ---
70
  def autonomous_research_agent(topic):
71
  arxiv = get_arxiv_papers(topic)
72
  scholar = get_semantic_scholar_papers(topic)
73
  web = search_duckduckgo(topic)
74
+ images = get_image_urls(topic)
75
 
76
+ image_md = ""
77
+ if images:
78
+ for img in images:
79
+ image_md += f"![Image]({img}) "
80
+ image_md += "\n\n"
81
 
82
+ arxiv_md = ""
 
 
 
83
  for p in arxiv:
84
+ arxiv_md += f"- [{p['title']}]({p['url']})\n> {p['summary'][:300]}...\n\n"
85
 
86
+ scholar_md = ""
87
  for p in scholar:
88
+ scholar_md += f"- [{p['title']}]({p['url']})\n> {p['summary'][:300]}...\n\n"
89
 
90
+ web_md = ""
91
  for w in web:
92
+ web_md += f"- [{w['title']}]({w['url']})\n> {w['snippet']}\n\n"
93
+
94
+ prompt = f"""
95
+ # Research Topic: {topic}
96
+
97
+ {image_md}
98
+
99
+ ## ArXiv:
100
+ {arxiv_md}
101
+
102
+ ## Semantic Scholar:
103
+ {scholar_md}
104
 
105
+ ## Web Insights:
106
+ {web_md}
 
 
 
 
 
107
 
108
+ Now synthesize this information into:
109
+ 1. A research gap
110
+ 2. A novel research direction
111
+ 3. A full markdown-formatted research article (continuous, no section labels, academic tone)
112
+ """
113
+ response = call_llm([{"role": "user", "content": prompt}], max_tokens=3000)
114
 
115
+ # Append Sources
116
+ response += "\n\n---\n### Sources Cited\n"
117
+ if arxiv_md:
118
+ response += "**ArXiv:**\n" + arxiv_md
119
+ if scholar_md:
120
+ response += "**Semantic Scholar:**\n" + scholar_md
121
+ if web_md:
122
+ response += "**Web:**\n" + web_md
123
+
124
+ return response
125
+
126
+ # --- Streamlit UI ---
127
+ st.set_page_config("Autonomous Research Assistant", layout="wide")
128
  st.title("🤖 Autonomous AI Research Assistant")
129
 
130
  if "chat_history" not in st.session_state:
131
  st.session_state.chat_history = []
132
 
133
  topic = st.text_input("Enter a research topic:")
134
+ if st.button("Run Research Agent"):
135
  with st.spinner("Gathering sources & thinking..."):
136
  try:
137
+ result = autonomous_research_agent(topic)
138
  st.session_state.chat_history.append({"role": "user", "content": topic})
139
+ st.session_state.chat_history.append({"role": "assistant", "content": result})
140
+ st.markdown(result)
141
  except Exception as e:
142
  st.error(f"Failed: {e}")
143
 
144
+ # --- Follow-up Q&A ---
145
  st.divider()
146
+ st.subheader("💬 Follow-up Q&A")
147
+ followup = st.text_input("Ask a follow-up question:")
148
  if st.button("Ask"):
149
  if followup:
150
+ try:
151
+ chat = st.session_state.chat_history + [{"role": "user", "content": followup}]
152
+ answer = call_llm(chat, max_tokens=1500)
153
+ st.session_state.chat_history.append({"role": "user", "content": followup})
154
+ st.session_state.chat_history.append({"role": "assistant", "content": answer})
155
+ st.markdown(answer)
156
+ except Exception as e:
157
+ st.error(f"Follow-up error: {e}")