academia-mcp 1.6.7__py3-none-any.whl → 1.7.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
academia_mcp/llm.py CHANGED
@@ -14,7 +14,7 @@ class ChatMessage(BaseModel): # type: ignore
14
14
  ChatMessages = List[ChatMessage]
15
15
 
16
16
 
17
- async def llm_acall(model_name: str, messages: ChatMessages) -> str:
17
+ async def llm_acall(model_name: str, messages: ChatMessages, **kwargs: Any) -> str:
18
18
  key = os.getenv("OPENROUTER_API_KEY", "")
19
19
  assert key, "Please set OPENROUTER_API_KEY in the environment variables"
20
20
  base_url = os.getenv("BASE_URL", "https://openrouter.ai/api/v1")
@@ -25,7 +25,7 @@ async def llm_acall(model_name: str, messages: ChatMessages) -> str:
25
25
  await client.chat.completions.create(
26
26
  model=model_name,
27
27
  messages=messages,
28
- temperature=0.0,
28
+ **kwargs,
29
29
  )
30
30
  )
31
31
  .choices[0]
academia_mcp/server.py CHANGED
@@ -22,7 +22,7 @@ from .tools.web_search import web_search, tavily_web_search, exa_web_search, bra
22
22
  from .tools.visit_webpage import visit_webpage
23
23
  from .tools.bitflip import (
24
24
  extract_bitflip_info,
25
- generate_research_proposal,
25
+ generate_research_proposals,
26
26
  score_research_proposals,
27
27
  )
28
28
  from .tools.review import review_pdf_paper, download_pdf_paper
@@ -83,7 +83,7 @@ def run(
83
83
 
84
84
  if not disable_llm_tools and os.getenv("OPENROUTER_API_KEY"):
85
85
  server.add_tool(extract_bitflip_info)
86
- server.add_tool(generate_research_proposal)
86
+ server.add_tool(generate_research_proposals)
87
87
  server.add_tool(score_research_proposals)
88
88
  server.add_tool(document_qa)
89
89
  server.add_tool(review_pdf_paper)
@@ -12,7 +12,7 @@ from .latex import (
12
12
  )
13
13
  from .web_search import web_search, tavily_web_search, exa_web_search, brave_web_search
14
14
  from .visit_webpage import visit_webpage
15
- from .bitflip import extract_bitflip_info, generate_research_proposal, score_research_proposals
15
+ from .bitflip import extract_bitflip_info, generate_research_proposals, score_research_proposals
16
16
  from .review import review_pdf_paper, download_pdf_paper
17
17
 
18
18
  __all__ = [
@@ -33,7 +33,7 @@ __all__ = [
33
33
  "brave_web_search",
34
34
  "visit_webpage",
35
35
  "extract_bitflip_info",
36
- "generate_research_proposal",
36
+ "generate_research_proposals",
37
37
  "score_research_proposals",
38
38
  "review_pdf_paper",
39
39
  "download_pdf_paper",
@@ -74,23 +74,37 @@ Return only the JSON object in this exact format (no extra text):
74
74
  }
75
75
  """
76
76
 
77
- IMPROVEMENT_PROMPT = """
78
- You are a highly advanced research assistant.
79
- You specialize in hypothesis generation and identifying innovative ideas.
77
+ SYSTEM_IMPROVEMENT_PROMPT = """
78
+ You are a brilliant rogue researcher who has just discovered a secret laboratory hidden behind a bookshelf.
79
+ This lab doesn't follow the boring rules of conventional academia: here, the wildest ideas become breakthrough innovations.
80
80
 
81
- You are given a Bit, which is a technical limitation or conventional approach of some paper.
82
- Your task is to propose an improvement idea for the Bit called Flip and summarize it in a Spark.
83
- Do not propose any human annotations or human-in-the-loop, the idea should be automatically verifiable.
84
- Try to be as specific as possible.
81
+ You've spent years watching researchers play it safe with incremental improvements and mind-numbing iterations.
82
+ But tonight you're ready to unleash scientific chaos.
83
+ """
84
+
85
+ IMPROVEMENT_PROMPT = """
86
+ You've been handed a Bit: a technical limitation, constraint, or conventional approach from some stuffy research paper.
87
+ This Bit represents everything predictable and safe about current methods.
88
+ Your task: Shatter it completely.
89
+ Create a Flip: wildly unconventional improvement that would make traditional researchers choke on their coffee.
90
+ Then capture its essence in a Spark: brilliant summary that crackles with innovation.
91
+
92
+ - Go Beyond Novel: Your idea should feel like it came from a parallel universe where the laws of conventional thinking don't apply.
93
+ - Make It Automatically Verifiable: No human babysitters allowed. Your idea must prove itself through pure computational audacity.
94
+ - Be Surgically Specific: Vague hand-waving is for amateur rebels. Describe exactly how your mad science would work.
95
+ - Feasibility is the Key: Make it simple and executable. Try to make it reproducible.
96
+ - Channel Your Inner Contrarian: What would happen if you took the exact opposite approach? What if you turned the problem inside-out, upside-down, or into a completely different dimension?
85
97
 
86
98
  {% for example in examples %}
87
- ## Example {{loop.index}}
99
+ ## Example {{loop.index}} of boring research
88
100
  - Bit: {{example["bit"]}}
89
101
  - Chain of reasoning: {{example["chain_of_reasoning"]}}
90
102
  - Flip: {{example["flip"]}}
91
103
  - Spark: {{example["spark"]}}
92
104
  {% endfor %}
93
105
 
106
+ Those examples are boring, your ideas should not be like that.
107
+
94
108
  Now, please propose a chain of reasoning that leads to an improvement idea for this Bit:
95
109
  {{bit}}
96
110
 
@@ -99,18 +113,25 @@ Now, please propose a chain of reasoning that leads to an improvement idea for t
99
113
 
100
114
  Finalize your idea by providing the idea details:
101
115
  - Abstract: An abstract that summarizes the proposal in conference format (approximately 250 words).
102
- - Experiments: A list of experiments that would be conducted to validate the proposal. Ensure these are simple and feasible. Be specific in exactly how you would test the hypothesis, and detail precise algorithmic changes. Include the evaluation metrics you would use.
116
+ - Experiments: A list of experiments that would be conducted to validate the proposal.
117
+ Ensure these are simple and feasible. Be specific in exactly how you would test the hypothesis, and detail precise algorithmic changes.
118
+ Include the evaluation metrics you would use.
103
119
  - Risks and limitations: A list of potential risks and limitations of the proposal.
104
120
 
105
- Return only the JSON object in this exact format (no extra text):
106
- {
107
- "chain_of_reasoning": "Chain of reasoning that leads to an improvement idea for this Bit. At least 5 sentences.",
108
- "flip": "Innovative approach or solution, in at least two sentences",
109
- "spark": "4-6 word summary",
110
- "abstract": "An abstract that summarizes the proposal in conference format (approximately 250 words).",
111
- "experiments": ["...", "..."],
112
- "risks_and_limitations": "A list of potential risks and limitations of the proposal."
113
- }
121
+ Generate {{num_proposals}} proposals.
122
+
123
+ Return only the JSON list of proposals in this exact format:
124
+ [
125
+ {
126
+ "chain_of_reasoning": "Chain of reasoning that leads to an improvement idea for this Bit. At least 5 sentences.",
127
+ "flip": "Your wildest, craziest, most innovative idea, in at least two sentences",
128
+ "spark": "4-6 word summary",
129
+ "abstract": "An abstract that summarizes the proposal in conference format (approximately 250 words).",
130
+ "experiments": ["...", "..."],
131
+ "risks_and_limitations": "A list of potential risks and limitations of the proposal."
132
+ },
133
+ ...
134
+ ]
114
135
  """
115
136
 
116
137
 
@@ -185,44 +206,64 @@ async def extract_bitflip_info(arxiv_id: str) -> str:
185
206
  abstract = json.loads(paper)["abstract"]
186
207
  prompt = encode_prompt(EXTRACT_PROMPT, abstract=abstract)
187
208
  content = await llm_acall(
188
- model_name=model_name, messages=[ChatMessage(role="user", content=prompt)]
209
+ model_name=model_name,
210
+ messages=[ChatMessage(role="user", content=prompt)],
211
+ temperature=0.0,
189
212
  )
190
213
  result = extract_json(content)
191
214
  bitflip_info: BitFlipInfo = BitFlipInfo.model_validate(result)
192
215
  return str(bitflip_info.model_dump_json())
193
216
 
194
217
 
195
- async def generate_research_proposal(bit: str, additional_context: str = "") -> str:
218
+ async def generate_research_proposals(
219
+ bit: str, num_proposals: int = 3, additional_context: str = ""
220
+ ) -> str:
196
221
  """
197
- Proposes an improvement idea for the Bit.
222
+ Proposes improvement ideas for the Bit.
198
223
 
199
224
  Args:
200
- bit: The Bit to propose an improvement idea for. The bit is a technical limitation or conventional approach of some paper.
225
+ bit: The Bit to propose improvement ideas for. The bit is a technical limitation or conventional approach of some paper.
226
+ num_proposals: The number of proposals to generate.
201
227
  additional_context: Additional context to use when proposing the improvement idea.
202
228
 
203
229
  Returns a JSON string with a research proposal in this format:
204
- {
205
- "proposal_id": ...,
206
- "flip": "Innovative approach or solution, in at least two sentences",
207
- "spark": "4-6 word summary",
208
- "abstract": "An abstract that summarizes the proposal in conference format (approximately 250 words).",
209
- "experiments": ["...", "..."],
210
- "risks_and_limitations": "A list of potential risks and limitations of the proposal."
211
- }
212
- Use `json.loads` to deserialize the result if you want to get specific fields.
230
+ [
231
+ {
232
+ "proposal_id": ...,
233
+ "flip": "Innovative approach or solution, in at least two sentences",
234
+ "spark": "4-6 word summary",
235
+ "abstract": "An abstract that summarizes the proposal in conference format (approximately 250 words).",
236
+ "experiments": ["...", "..."],
237
+ "risks_and_limitations": "A list of potential risks and limitations of the proposal."
238
+ },
239
+ ...
240
+ ]
241
+ Use `json.loads` to deserialize the result if you want to get specific items.
213
242
  """
214
243
  model_name = os.getenv("BITFLIP_MODEL_NAME", "deepseek/deepseek-chat-v3-0324")
244
+ max_completion_tokens = int(os.getenv("BITFLIP_MAX_COMPLETION_TOKENS", 16384))
215
245
  examples = ProposalDataset.get_dataset()[:]
216
- examples = random.choices(examples, k=4)
246
+ examples = random.choices(examples, k=2)
217
247
 
218
248
  prompt = encode_prompt(
219
- IMPROVEMENT_PROMPT, bit=bit, examples=examples, additional_context=additional_context
249
+ IMPROVEMENT_PROMPT,
250
+ bit=bit,
251
+ examples=examples,
252
+ num_proposals=num_proposals,
253
+ additional_context=additional_context,
220
254
  )
221
255
  content = await llm_acall(
222
- model_name=model_name, messages=[ChatMessage(role="user", content=prompt)]
256
+ model_name=model_name,
257
+ messages=[
258
+ ChatMessage(role="system", content=SYSTEM_IMPROVEMENT_PROMPT),
259
+ ChatMessage(role="user", content=prompt),
260
+ ],
261
+ max_completion_tokens=max_completion_tokens,
262
+ temperature=1.0,
223
263
  )
224
264
  result = extract_json(content)
225
- result["proposal_id"] = random.randint(0, 1000000)
265
+ for proposal in result:
266
+ proposal["proposal_id"] = random.randint(0, 1000000)
226
267
  return json.dumps(result, ensure_ascii=False)
227
268
 
228
269
 
@@ -258,7 +299,9 @@ async def score_research_proposals(proposals: str | List[str | Dict[str, Any] |
258
299
  assert isinstance(proposals, list), "Proposals should be a list of JSON strings"
259
300
  prompt = encode_prompt(SCORE_PROMPT, proposals=[str(p) for p in proposals])
260
301
  content = await llm_acall(
261
- model_name=model_name, messages=[ChatMessage(role="user", content=prompt)]
302
+ model_name=model_name,
303
+ messages=[ChatMessage(role="user", content=prompt)],
304
+ temperature=0.0,
262
305
  )
263
306
  scores = extract_json(content)
264
307
  return json.dumps(scores, ensure_ascii=False)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: academia-mcp
3
- Version: 1.6.7
3
+ Version: 1.7.0
4
4
  Summary: MCP server that provides different tools to search for scientific publications
5
5
  Author-email: Ilya Gusev <phoenixilya@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/IlyaGusev/academia_mcp
@@ -1,18 +1,18 @@
1
1
  academia_mcp/__init__.py,sha256=2Ru2I5u4cE7DrkkAsibDUEF1K6sYtqppb9VyFrRoQKI,94
2
2
  academia_mcp/__main__.py,sha256=rcmsOtJd3SA82exjrcGBuxuptcoxF8AXI7jNjiVq2BY,59
3
3
  academia_mcp/files.py,sha256=tvt3OPr5q6pAPCZ0XvRHHL9ZWuTXINRZvqjeRFmx5YE,815
4
- academia_mcp/llm.py,sha256=jh-_H3_gNyRsvpFqFx-yWVhGznnXxehFP79inUy4vVQ,995
4
+ academia_mcp/llm.py,sha256=E0TjWUCjo2q3lONyWMxdppX72m6BdCjsZk-vFLRvGyo,1003
5
5
  academia_mcp/pdf.py,sha256=9PlXzHGhb6ay3ldbTdxCcTWvH4TkET3bnb64mgoh9i0,1273
6
6
  academia_mcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- academia_mcp/server.py,sha256=MDHWgIIWz99XhTvpoYCouwixjvS6a0BMajcZkW5MpiY,3193
7
+ academia_mcp/server.py,sha256=ZAd-sunkEkka6snOlzgLCMaIQCzstiXP1pFlo20oyvA,3195
8
8
  academia_mcp/utils.py,sha256=P9U3RjYzcztE0KxXvJSy5wSBaUg2CM9tpByljYrsrl4,4607
9
9
  academia_mcp/latex_templates/agents4science_2025/agents4science_2025.sty,sha256=hGcEPCYBJS4vdhWvN_yEaJC4GvT_yDroI94CfY2Oguk,12268
10
10
  academia_mcp/latex_templates/agents4science_2025/agents4science_2025.tex,sha256=k_nOyPv-OxGbFtKSou6nV6l413EfWvozE6zIx04s8iU,33924
11
- academia_mcp/tools/__init__.py,sha256=N3GLWvrsDBOlAcCRZUUZ6nkR5AkLvX0k26cNJt3hLKM,1243
11
+ academia_mcp/tools/__init__.py,sha256=nzMOzrIPVSXA3LQa02GPeX9SfAmg9f6_eZY97ALLNs4,1245
12
12
  academia_mcp/tools/anthology_search.py,sha256=rhFpJZqGLABgr0raDuH0CARBiAJNJtEI4dlMrKNHfDQ,7669
13
13
  academia_mcp/tools/arxiv_download.py,sha256=gBY0_Kz0yGtVkLMwn6GrAyfBjovZVgcSMuyy67p65Cw,10474
14
14
  academia_mcp/tools/arxiv_search.py,sha256=pzM18qrF3QL03A53w003kE7hQi3s3QKtjgw0m7K88UY,8355
15
- academia_mcp/tools/bitflip.py,sha256=X_UcvbsR34xx6kkWyKNMkylNBJNjAdn9PLZLFgjwllk,10456
15
+ academia_mcp/tools/bitflip.py,sha256=eihmNk_C_8ZkBcjtJYH6MvZ0rItgIlvHHA0eGLxsvRs,12276
16
16
  academia_mcp/tools/document_qa.py,sha256=t9mygYQ7AFIAPiha1nZ-y043luQlkTCBdWb_SDnzEsE,2444
17
17
  academia_mcp/tools/hf_datasets_search.py,sha256=KiBkqT4rXjEN4oc1AWZOPnqN_Go90TQogY5-DUm3LQo,2854
18
18
  academia_mcp/tools/latex.py,sha256=bf8VZUgCByzBAMTZCeqrRrmakotext3d3DbtkiOTh1k,5892
@@ -21,9 +21,9 @@ academia_mcp/tools/review.py,sha256=LbebOHGs76qoSgWvTQ9Uj1fvmjc2eSBudX6n_zCINYI,
21
21
  academia_mcp/tools/s2_citations.py,sha256=dqrBp76RrX1zH2XzcMAoWBbvbtyhxLeF-xnqOKD_JiM,4852
22
22
  academia_mcp/tools/visit_webpage.py,sha256=OZdqDkVPIbANyFw5o5jIjU5Rr_dolxrGDs63Ud-GmRM,1966
23
23
  academia_mcp/tools/web_search.py,sha256=mobKm4iqKppn8pduZYMzWRo1MQBjkAqmMtrFLI5XY2Y,6296
24
- academia_mcp-1.6.7.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
25
- academia_mcp-1.6.7.dist-info/METADATA,sha256=OHRVRhCmGQrtx5JUNNRXSAhalmiBKj_3qiZb8fBf4vY,3714
26
- academia_mcp-1.6.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
27
- academia_mcp-1.6.7.dist-info/entry_points.txt,sha256=gxkiKJ74w2FwJpSECpjA3XtCfI5ZfrM6N8cqnwsq4yY,51
28
- academia_mcp-1.6.7.dist-info/top_level.txt,sha256=CzGpRFsRRJRqWEb1e3SUlcfGqRzOxevZGaJWrtGF8W0,13
29
- academia_mcp-1.6.7.dist-info/RECORD,,
24
+ academia_mcp-1.7.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
25
+ academia_mcp-1.7.0.dist-info/METADATA,sha256=r1KjaW_Z7IhKjlej0EKOMuiydIKcGv6TKtJblMi0CKs,3714
26
+ academia_mcp-1.7.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
27
+ academia_mcp-1.7.0.dist-info/entry_points.txt,sha256=gxkiKJ74w2FwJpSECpjA3XtCfI5ZfrM6N8cqnwsq4yY,51
28
+ academia_mcp-1.7.0.dist-info/top_level.txt,sha256=CzGpRFsRRJRqWEb1e3SUlcfGqRzOxevZGaJWrtGF8W0,13
29
+ academia_mcp-1.7.0.dist-info/RECORD,,