academia-mcp 1.6.6__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
 
@@ -161,28 +182,6 @@ class BitFlipInfo(BaseModel): # type: ignore
161
182
  spark: str
162
183
 
163
184
 
164
- class Proposal(BaseModel): # type: ignore
165
- proposal_id: Optional[int] = None
166
- flip: str
167
- spark: str
168
- abstract: str
169
- experiments: List[str]
170
- risks_and_limitations: List[str]
171
-
172
-
173
- class ProposalScores(BaseModel): # type: ignore
174
- proposal_id: int
175
- spark: str
176
- strengths: List[str]
177
- weaknesses: List[str]
178
- novelty: int
179
- clarity: int
180
- significance: int
181
- feasibility: int
182
- soundness: int
183
- overall: int
184
-
185
-
186
185
  async def extract_bitflip_info(arxiv_id: str) -> str:
187
186
  """
188
187
  Extracts the Bit-Flip information from the arXiv paper.
@@ -207,46 +206,65 @@ async def extract_bitflip_info(arxiv_id: str) -> str:
207
206
  abstract = json.loads(paper)["abstract"]
208
207
  prompt = encode_prompt(EXTRACT_PROMPT, abstract=abstract)
209
208
  content = await llm_acall(
210
- 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,
211
212
  )
212
213
  result = extract_json(content)
213
214
  bitflip_info: BitFlipInfo = BitFlipInfo.model_validate(result)
214
215
  return str(bitflip_info.model_dump_json())
215
216
 
216
217
 
217
- 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:
218
221
  """
219
- Proposes an improvement idea for the Bit.
222
+ Proposes improvement ideas for the Bit.
220
223
 
221
224
  Args:
222
- 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.
223
227
  additional_context: Additional context to use when proposing the improvement idea.
224
228
 
225
229
  Returns a JSON string with a research proposal in this format:
226
- {
227
- "proposal_id": ...,
228
- "flip": "Innovative approach or solution, in at least two sentences",
229
- "spark": "4-6 word summary",
230
- "abstract": "An abstract that summarizes the proposal in conference format (approximately 250 words).",
231
- "experiments": ["...", "..."],
232
- "risks_and_limitations": "A list of potential risks and limitations of the proposal."
233
- }
234
- 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.
235
242
  """
236
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))
237
245
  examples = ProposalDataset.get_dataset()[:]
238
- examples = random.choices(examples, k=4)
246
+ examples = random.choices(examples, k=2)
239
247
 
240
248
  prompt = encode_prompt(
241
- 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,
242
254
  )
243
255
  content = await llm_acall(
244
- 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,
245
263
  )
246
264
  result = extract_json(content)
247
- proposal: Proposal = Proposal.model_validate(result)
248
- proposal.proposal_id = random.randint(0, 1000000)
249
- return str(proposal.model_dump_json())
265
+ for proposal in result:
266
+ proposal["proposal_id"] = random.randint(0, 1000000)
267
+ return json.dumps(result, ensure_ascii=False)
250
268
 
251
269
 
252
270
  async def score_research_proposals(proposals: str | List[str | Dict[str, Any] | Any]) -> str:
@@ -281,8 +299,9 @@ async def score_research_proposals(proposals: str | List[str | Dict[str, Any] |
281
299
  assert isinstance(proposals, list), "Proposals should be a list of JSON strings"
282
300
  prompt = encode_prompt(SCORE_PROMPT, proposals=[str(p) for p in proposals])
283
301
  content = await llm_acall(
284
- 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,
285
305
  )
286
306
  scores = extract_json(content)
287
- final_scores = [ProposalScores.model_validate(score) for score in scores]
288
- return json.dumps([s.model_dump() for s in final_scores], ensure_ascii=False)
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.6
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=3qpdx9rdrs-BSSWzalM9fdaS6vzP2AwPWekjXFrKTGs,11060
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.6.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
25
- academia_mcp-1.6.6.dist-info/METADATA,sha256=yKe0Bx9m8R483xM9ffGsRWV83tRmrTdA3CklsEVUTrg,3714
26
- academia_mcp-1.6.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
27
- academia_mcp-1.6.6.dist-info/entry_points.txt,sha256=gxkiKJ74w2FwJpSECpjA3XtCfI5ZfrM6N8cqnwsq4yY,51
28
- academia_mcp-1.6.6.dist-info/top_level.txt,sha256=CzGpRFsRRJRqWEb1e3SUlcfGqRzOxevZGaJWrtGF8W0,13
29
- academia_mcp-1.6.6.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,,