EvoScientist 0.0.1.dev3__py3-none-any.whl → 0.1.0rc1__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.
Files changed (108) hide show
  1. EvoScientist/EvoScientist.py +17 -49
  2. EvoScientist/backends.py +0 -26
  3. EvoScientist/cli.py +1109 -255
  4. EvoScientist/middleware.py +8 -61
  5. EvoScientist/stream/__init__.py +0 -25
  6. EvoScientist/stream/utils.py +16 -23
  7. EvoScientist/tools.py +0 -64
  8. evoscientist-0.1.0rc1.dist-info/METADATA +199 -0
  9. evoscientist-0.1.0rc1.dist-info/RECORD +21 -0
  10. evoscientist-0.1.0rc1.dist-info/entry_points.txt +2 -0
  11. EvoScientist/memory.py +0 -715
  12. EvoScientist/paths.py +0 -45
  13. EvoScientist/skills/accelerate/SKILL.md +0 -332
  14. EvoScientist/skills/accelerate/references/custom-plugins.md +0 -453
  15. EvoScientist/skills/accelerate/references/megatron-integration.md +0 -489
  16. EvoScientist/skills/accelerate/references/performance.md +0 -525
  17. EvoScientist/skills/bitsandbytes/SKILL.md +0 -411
  18. EvoScientist/skills/bitsandbytes/references/memory-optimization.md +0 -521
  19. EvoScientist/skills/bitsandbytes/references/qlora-training.md +0 -521
  20. EvoScientist/skills/bitsandbytes/references/quantization-formats.md +0 -447
  21. EvoScientist/skills/find-skills/SKILL.md +0 -133
  22. EvoScientist/skills/find-skills/scripts/install_skill.py +0 -211
  23. EvoScientist/skills/flash-attention/SKILL.md +0 -367
  24. EvoScientist/skills/flash-attention/references/benchmarks.md +0 -215
  25. EvoScientist/skills/flash-attention/references/transformers-integration.md +0 -293
  26. EvoScientist/skills/llama-cpp/SKILL.md +0 -258
  27. EvoScientist/skills/llama-cpp/references/optimization.md +0 -89
  28. EvoScientist/skills/llama-cpp/references/quantization.md +0 -213
  29. EvoScientist/skills/llama-cpp/references/server.md +0 -125
  30. EvoScientist/skills/lm-evaluation-harness/SKILL.md +0 -490
  31. EvoScientist/skills/lm-evaluation-harness/references/api-evaluation.md +0 -490
  32. EvoScientist/skills/lm-evaluation-harness/references/benchmark-guide.md +0 -488
  33. EvoScientist/skills/lm-evaluation-harness/references/custom-tasks.md +0 -602
  34. EvoScientist/skills/lm-evaluation-harness/references/distributed-eval.md +0 -519
  35. EvoScientist/skills/ml-paper-writing/SKILL.md +0 -937
  36. EvoScientist/skills/ml-paper-writing/references/checklists.md +0 -361
  37. EvoScientist/skills/ml-paper-writing/references/citation-workflow.md +0 -562
  38. EvoScientist/skills/ml-paper-writing/references/reviewer-guidelines.md +0 -367
  39. EvoScientist/skills/ml-paper-writing/references/sources.md +0 -159
  40. EvoScientist/skills/ml-paper-writing/references/writing-guide.md +0 -476
  41. EvoScientist/skills/ml-paper-writing/templates/README.md +0 -251
  42. EvoScientist/skills/ml-paper-writing/templates/aaai2026/README.md +0 -534
  43. EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026-unified-supp.tex +0 -144
  44. EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026-unified-template.tex +0 -952
  45. EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026.bib +0 -111
  46. EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026.bst +0 -1493
  47. EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026.sty +0 -315
  48. EvoScientist/skills/ml-paper-writing/templates/acl/README.md +0 -50
  49. EvoScientist/skills/ml-paper-writing/templates/acl/acl.sty +0 -312
  50. EvoScientist/skills/ml-paper-writing/templates/acl/acl_latex.tex +0 -377
  51. EvoScientist/skills/ml-paper-writing/templates/acl/acl_lualatex.tex +0 -101
  52. EvoScientist/skills/ml-paper-writing/templates/acl/acl_natbib.bst +0 -1940
  53. EvoScientist/skills/ml-paper-writing/templates/acl/anthology.bib.txt +0 -26
  54. EvoScientist/skills/ml-paper-writing/templates/acl/custom.bib +0 -70
  55. EvoScientist/skills/ml-paper-writing/templates/acl/formatting.md +0 -326
  56. EvoScientist/skills/ml-paper-writing/templates/colm2025/README.md +0 -3
  57. EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.bib +0 -11
  58. EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.bst +0 -1440
  59. EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.pdf +0 -0
  60. EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.sty +0 -218
  61. EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.tex +0 -305
  62. EvoScientist/skills/ml-paper-writing/templates/colm2025/fancyhdr.sty +0 -485
  63. EvoScientist/skills/ml-paper-writing/templates/colm2025/math_commands.tex +0 -508
  64. EvoScientist/skills/ml-paper-writing/templates/colm2025/natbib.sty +0 -1246
  65. EvoScientist/skills/ml-paper-writing/templates/iclr2026/fancyhdr.sty +0 -485
  66. EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.bib +0 -24
  67. EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.bst +0 -1440
  68. EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.pdf +0 -0
  69. EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.sty +0 -246
  70. EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.tex +0 -414
  71. EvoScientist/skills/ml-paper-writing/templates/iclr2026/math_commands.tex +0 -508
  72. EvoScientist/skills/ml-paper-writing/templates/iclr2026/natbib.sty +0 -1246
  73. EvoScientist/skills/ml-paper-writing/templates/icml2026/algorithm.sty +0 -79
  74. EvoScientist/skills/ml-paper-writing/templates/icml2026/algorithmic.sty +0 -201
  75. EvoScientist/skills/ml-paper-writing/templates/icml2026/example_paper.bib +0 -75
  76. EvoScientist/skills/ml-paper-writing/templates/icml2026/example_paper.pdf +0 -0
  77. EvoScientist/skills/ml-paper-writing/templates/icml2026/example_paper.tex +0 -662
  78. EvoScientist/skills/ml-paper-writing/templates/icml2026/fancyhdr.sty +0 -864
  79. EvoScientist/skills/ml-paper-writing/templates/icml2026/icml2026.bst +0 -1443
  80. EvoScientist/skills/ml-paper-writing/templates/icml2026/icml2026.sty +0 -767
  81. EvoScientist/skills/ml-paper-writing/templates/icml2026/icml_numpapers.pdf +0 -0
  82. EvoScientist/skills/ml-paper-writing/templates/neurips2025/Makefile +0 -36
  83. EvoScientist/skills/ml-paper-writing/templates/neurips2025/extra_pkgs.tex +0 -53
  84. EvoScientist/skills/ml-paper-writing/templates/neurips2025/main.tex +0 -38
  85. EvoScientist/skills/ml-paper-writing/templates/neurips2025/neurips.sty +0 -382
  86. EvoScientist/skills/peft/SKILL.md +0 -431
  87. EvoScientist/skills/peft/references/advanced-usage.md +0 -514
  88. EvoScientist/skills/peft/references/troubleshooting.md +0 -480
  89. EvoScientist/skills/ray-data/SKILL.md +0 -326
  90. EvoScientist/skills/ray-data/references/integration.md +0 -82
  91. EvoScientist/skills/ray-data/references/transformations.md +0 -83
  92. EvoScientist/skills/skill-creator/LICENSE.txt +0 -202
  93. EvoScientist/skills/skill-creator/SKILL.md +0 -356
  94. EvoScientist/skills/skill-creator/references/output-patterns.md +0 -82
  95. EvoScientist/skills/skill-creator/references/workflows.md +0 -28
  96. EvoScientist/skills/skill-creator/scripts/init_skill.py +0 -303
  97. EvoScientist/skills/skill-creator/scripts/package_skill.py +0 -110
  98. EvoScientist/skills/skill-creator/scripts/quick_validate.py +0 -95
  99. EvoScientist/skills_manager.py +0 -392
  100. EvoScientist/stream/display.py +0 -604
  101. EvoScientist/stream/events.py +0 -415
  102. EvoScientist/stream/state.py +0 -343
  103. evoscientist-0.0.1.dev3.dist-info/METADATA +0 -321
  104. evoscientist-0.0.1.dev3.dist-info/RECORD +0 -113
  105. evoscientist-0.0.1.dev3.dist-info/entry_points.txt +0 -5
  106. {evoscientist-0.0.1.dev3.dist-info → evoscientist-0.1.0rc1.dist-info}/WHEEL +0 -0
  107. {evoscientist-0.0.1.dev3.dist-info → evoscientist-0.1.0rc1.dist-info}/licenses/LICENSE +0 -0
  108. {evoscientist-0.0.1.dev3.dist-info → evoscientist-0.1.0rc1.dist-info}/top_level.txt +0 -0
@@ -1,343 +0,0 @@
1
- """Stream state tracking for CLI display.
2
-
3
- Contains SubAgentState, StreamState, and todo-item parsing helpers.
4
- No Rich dependencies — stdlib only.
5
- """
6
-
7
- import ast
8
- import json
9
-
10
-
11
- class SubAgentState:
12
- """Tracks a single sub-agent's activity."""
13
-
14
- def __init__(self, name: str, description: str = ""):
15
- self.name = name
16
- self.description = description
17
- self.tool_calls: list[dict] = []
18
- self.tool_results: list[dict] = []
19
- self._result_map: dict[str, dict] = {} # tool_call_id -> result
20
- self.is_active = True
21
-
22
- def add_tool_call(self, name: str, args: dict, tool_id: str = ""):
23
- # Skip empty-name calls without an id (incomplete streaming chunks)
24
- if not name and not tool_id:
25
- return
26
- tc_data = {"id": tool_id, "name": name, "args": args}
27
- if tool_id:
28
- for i, tc in enumerate(self.tool_calls):
29
- if tc.get("id") == tool_id:
30
- # Merge: keep the non-empty name/args
31
- if name:
32
- self.tool_calls[i]["name"] = name
33
- if args:
34
- self.tool_calls[i]["args"] = args
35
- return
36
- # Skip if name is empty and we can't deduplicate by id
37
- if not name:
38
- return
39
- self.tool_calls.append(tc_data)
40
-
41
- def add_tool_result(self, name: str, content: str, success: bool = True):
42
- result = {"name": name, "content": content, "success": success}
43
- self.tool_results.append(result)
44
- # Try to match result to the first unmatched tool call with same name
45
- for tc in self.tool_calls:
46
- tc_id = tc.get("id", "")
47
- tc_name = tc.get("name", "")
48
- if tc_id and tc_id not in self._result_map and tc_name == name:
49
- self._result_map[tc_id] = result
50
- return
51
- # Fallback: match first unmatched tool call
52
- for tc in self.tool_calls:
53
- tc_id = tc.get("id", "")
54
- if tc_id and tc_id not in self._result_map:
55
- self._result_map[tc_id] = result
56
- return
57
-
58
- def get_result_for(self, tc: dict) -> dict | None:
59
- """Get matched result for a tool call."""
60
- tc_id = tc.get("id", "")
61
- if tc_id:
62
- return self._result_map.get(tc_id)
63
- # Fallback: index-based matching
64
- try:
65
- idx = self.tool_calls.index(tc)
66
- if idx < len(self.tool_results):
67
- return self.tool_results[idx]
68
- except ValueError:
69
- pass
70
- return None
71
-
72
-
73
- class StreamState:
74
- """Accumulates stream state for display updates."""
75
-
76
- def __init__(self):
77
- self.thinking_text = ""
78
- self.response_text = ""
79
- self.tool_calls = []
80
- self.tool_results = []
81
- self.is_thinking = False
82
- self.is_responding = False
83
- self.is_processing = False
84
- # Sub-agent tracking
85
- self.subagents: list[SubAgentState] = []
86
- self._subagent_map: dict[str, SubAgentState] = {} # name -> state
87
- # Todo list tracking
88
- self.todo_items: list[dict] = []
89
- # Latest text segment (reset on each tool_call)
90
- self.latest_text = ""
91
-
92
- def _get_or_create_subagent(self, name: str, description: str = "") -> SubAgentState:
93
- if name not in self._subagent_map:
94
- # Case 1: real name arrives, "sub-agent" entry exists -> rename it
95
- if name != "sub-agent" and "sub-agent" in self._subagent_map:
96
- old_sa = self._subagent_map.pop("sub-agent")
97
- old_sa.name = name
98
- if description:
99
- old_sa.description = description
100
- self._subagent_map[name] = old_sa
101
- return old_sa
102
- # Case 2: "sub-agent" arrives but a pre-registered real-name entry
103
- # exists with no tool calls -> merge into it
104
- if name == "sub-agent":
105
- active_named = [
106
- sa for sa in self.subagents
107
- if sa.is_active and sa.name != "sub-agent"
108
- ]
109
- if len(active_named) == 1 and not active_named[0].tool_calls:
110
- self._subagent_map[name] = active_named[0]
111
- return active_named[0]
112
- sa = SubAgentState(name, description)
113
- self.subagents.append(sa)
114
- self._subagent_map[name] = sa
115
- else:
116
- existing = self._subagent_map[name]
117
- if description and not existing.description:
118
- existing.description = description
119
- # If this entry was created as "sub-agent" placeholder and the
120
- # actual name is different, update.
121
- if name != "sub-agent" and existing.name == "sub-agent":
122
- existing.name = name
123
- return self._subagent_map[name]
124
-
125
- def _resolve_subagent_name(self, name: str) -> str:
126
- """Resolve "sub-agent" to the single active named sub-agent when possible."""
127
- if name != "sub-agent":
128
- return name
129
- active_named = [
130
- sa.name for sa in self.subagents
131
- if sa.is_active and sa.name != "sub-agent"
132
- ]
133
- if len(active_named) == 1:
134
- return active_named[0]
135
- return name
136
-
137
- def handle_event(self, event: dict) -> str:
138
- """Process a single stream event, update internal state, return event type."""
139
- event_type: str = event.get("type", "")
140
-
141
- if event_type == "thinking":
142
- self.is_thinking = True
143
- self.is_responding = False
144
- self.is_processing = False
145
- self.thinking_text += event.get("content", "")
146
-
147
- elif event_type == "text":
148
- self.is_thinking = False
149
- self.is_responding = True
150
- self.is_processing = False
151
- text_content = event.get("content", "")
152
- self.response_text += text_content
153
- self.latest_text += text_content
154
-
155
- elif event_type == "tool_call":
156
- self.is_thinking = False
157
- self.is_responding = False
158
- self.is_processing = False
159
- self.latest_text = "" # Reset -- next text segment is a new message
160
-
161
- tool_id = event.get("id", "")
162
- tool_name = event.get("name", "unknown")
163
- tool_args = event.get("args", {})
164
- tc_data = {
165
- "id": tool_id,
166
- "name": tool_name,
167
- "args": tool_args,
168
- }
169
-
170
- if tool_id:
171
- updated = False
172
- for i, tc in enumerate(self.tool_calls):
173
- if tc.get("id") == tool_id:
174
- self.tool_calls[i] = tc_data
175
- updated = True
176
- break
177
- if not updated:
178
- self.tool_calls.append(tc_data)
179
- else:
180
- self.tool_calls.append(tc_data)
181
-
182
- # Capture todo items from write_todos args (most reliable source)
183
- if tool_name == "write_todos":
184
- todos = tool_args.get("todos", [])
185
- if isinstance(todos, list) and todos:
186
- self.todo_items = todos
187
-
188
- elif event_type == "tool_result":
189
- self.is_processing = True
190
- result_name = event.get("name", "unknown")
191
- result_content = event.get("content", "")
192
- self.tool_results.append({
193
- "name": result_name,
194
- "content": result_content,
195
- })
196
- # Update todo list from write_todos / read_todos results (fallback)
197
- if result_name in ("write_todos", "read_todos"):
198
- parsed = _parse_todo_items(result_content)
199
- if parsed:
200
- self.todo_items = parsed
201
-
202
- elif event_type == "subagent_start":
203
- name = event.get("name", "sub-agent")
204
- desc = event.get("description", "")
205
- sa = self._get_or_create_subagent(name, desc)
206
- sa.is_active = True
207
-
208
- elif event_type == "subagent_tool_call":
209
- sa_name = self._resolve_subagent_name(event.get("subagent", "sub-agent"))
210
- sa = self._get_or_create_subagent(sa_name)
211
- sa.add_tool_call(
212
- event.get("name", "unknown"),
213
- event.get("args", {}),
214
- event.get("id", ""),
215
- )
216
-
217
- elif event_type == "subagent_tool_result":
218
- sa_name = self._resolve_subagent_name(event.get("subagent", "sub-agent"))
219
- sa = self._get_or_create_subagent(sa_name)
220
- sa.add_tool_result(
221
- event.get("name", "unknown"),
222
- event.get("content", ""),
223
- event.get("success", True),
224
- )
225
-
226
- elif event_type == "subagent_end":
227
- name = self._resolve_subagent_name(event.get("name", "sub-agent"))
228
- if name in self._subagent_map:
229
- self._subagent_map[name].is_active = False
230
- elif name == "sub-agent":
231
- # Couldn't resolve -- deactivate the oldest active sub-agent
232
- for sa in self.subagents:
233
- if sa.is_active:
234
- sa.is_active = False
235
- break
236
-
237
- elif event_type == "done":
238
- self.is_processing = False
239
- if not self.response_text:
240
- self.response_text = event.get("response", "")
241
-
242
- elif event_type == "error":
243
- self.is_processing = False
244
- self.is_thinking = False
245
- self.is_responding = False
246
- error_msg = event.get("message", "Unknown error")
247
- self.response_text += f"\n\n[Error] {error_msg}"
248
-
249
- return event_type
250
-
251
- def get_display_args(self) -> dict:
252
- """Get kwargs for create_streaming_display()."""
253
- return {
254
- "thinking_text": self.thinking_text,
255
- "response_text": self.response_text,
256
- "latest_text": self.latest_text,
257
- "tool_calls": self.tool_calls,
258
- "tool_results": self.tool_results,
259
- "is_thinking": self.is_thinking,
260
- "is_responding": self.is_responding,
261
- "is_processing": self.is_processing,
262
- "subagents": self.subagents,
263
- "todo_items": self.todo_items,
264
- }
265
-
266
-
267
- def _parse_todo_items(content: str) -> list[dict] | None:
268
- """Parse todo items from write_todos output.
269
-
270
- Attempts to extract a list of dicts with 'status' and 'content' keys
271
- from the tool result string. Returns None if parsing fails.
272
-
273
- Handles formats like:
274
- - Raw JSON/Python list: [{"content": "...", "status": "..."}]
275
- - Prefixed: "Updated todo list to [{'content': '...', ...}]"
276
- """
277
- content = content.strip()
278
-
279
- def _try_parse(text: str) -> list[dict] | None:
280
- """Try JSON then Python literal parsing."""
281
- text = text.strip()
282
- try:
283
- data = json.loads(text)
284
- if isinstance(data, list) and data and isinstance(data[0], dict):
285
- return data
286
- except (json.JSONDecodeError, ValueError):
287
- pass
288
- try:
289
- data = ast.literal_eval(text)
290
- if isinstance(data, list) and data and isinstance(data[0], dict):
291
- return data
292
- except (ValueError, SyntaxError):
293
- pass
294
- return None
295
-
296
- # Try the full content directly
297
- result = _try_parse(content)
298
- if result:
299
- return result
300
-
301
- # Extract embedded [...] from content (e.g. "Updated todo list to [{...}]")
302
- bracket_start = content.find("[")
303
- if bracket_start != -1:
304
- bracket_end = content.rfind("]")
305
- if bracket_end > bracket_start:
306
- embedded = content[bracket_start:bracket_end + 1]
307
- result = _try_parse(embedded)
308
- if result:
309
- return result
310
-
311
- # Try line-by-line scan
312
- for line in content.split("\n"):
313
- line = line.strip()
314
- if "[" in line:
315
- start = line.find("[")
316
- end = line.rfind("]")
317
- if end > start:
318
- result = _try_parse(line[start:end + 1])
319
- if result:
320
- return result
321
-
322
- return None
323
-
324
-
325
- def _build_todo_stats(items: list[dict]) -> str:
326
- """Build stats string like '2 active | 1 pending | 3 done'."""
327
- counts: dict[str, int] = {}
328
- for item in items:
329
- status = str(item.get("status", "todo")).lower()
330
- # Normalize status names
331
- if status in ("done", "completed", "complete"):
332
- status = "done"
333
- elif status in ("active", "in_progress", "in-progress", "working"):
334
- status = "active"
335
- else:
336
- status = "pending"
337
- counts[status] = counts.get(status, 0) + 1
338
-
339
- parts = []
340
- for key in ("active", "pending", "done"):
341
- if counts.get(key, 0) > 0:
342
- parts.append(f"{counts[key]} {key}")
343
- return " | ".join(parts) if parts else f"{len(items)} items"
@@ -1,321 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: EvoScientist
3
- Version: 0.0.1.dev3
4
- Summary: EvoScientist: Towards Self-Evolving AI Scientists for End-to-End Scientific Discovery
5
- Author: Xi Zhang
6
- Maintainer: Xi Zhang
7
- License-Expression: MIT
8
- Project-URL: Homepage, https://github.com/EvoScientist/EvoScientist
9
- Project-URL: Bug Tracker, https://github.com/EvoScientist/EvoScientist/issues
10
- Project-URL: Documentation, https://github.com/EvoScientist/EvoScientist#readme
11
- Keywords: ai-scientific,scientific-discovery,self-evolving,ai-scientists,end-to-end
12
- Classifier: Programming Language :: Python :: 3
13
- Requires-Python: >=3.11
14
- Description-Content-Type: text/markdown
15
- License-File: LICENSE
16
- Requires-Dist: deepagents>=0.3.6
17
- Requires-Dist: langchain>=1.2
18
- Requires-Dist: langchain-anthropic>=1.3
19
- Requires-Dist: anthropic>=0.76
20
- Requires-Dist: tavily-python>=0.7
21
- Requires-Dist: pyyaml>=6.0
22
- Requires-Dist: rich>=14.0
23
- Requires-Dist: prompt-toolkit>=3.0
24
- Requires-Dist: typer>=0.12
25
- Requires-Dist: python-dotenv>=1.0
26
- Requires-Dist: langgraph-cli[inmem]>=0.4
27
- Requires-Dist: httpx>=0.27
28
- Requires-Dist: markdownify>=0.14
29
- Provides-Extra: dev
30
- Requires-Dist: pytest>=8.0; extra == "dev"
31
- Requires-Dist: pytest-cov>=5.0; extra == "dev"
32
- Dynamic: license-file
33
-
34
- <!-- Add logo here -->
35
- <h1 align="center">
36
- <!-- <img src="./assets/CCD_icon_logo.png" alt="CCD Logo" height="27" style="position: relative; top: -2px;"/> -->
37
- <strong>EvoScientist</strong>
38
- </h1>
39
-
40
-
41
- <div align="center">
42
-
43
- <a href="https://git.io/typing-svg"><img src="https://readme-typing-svg.demolab.com?font=Fira+Code&pause=1000&width=435&lines=Towards+Self-Evolving+AI+Scientists+for+End-to-End+Scientific+Discovery" alt="Typing SVG" /></a>
44
-
45
- [![PyPI](https://img.shields.io/badge/PyPI-EvoScientist%20v0.0.1-3da9fc?style=for-the-badge&logo=python&logoColor=3da9fc)](https://pypi.org/project/EvoScientist/)
46
- [![Project Page](https://img.shields.io/badge/Project-Page-ff8e3c?style=for-the-badge&logo=googlelens&logoColor=ff8e3c)]()
47
- [![arXiv](https://img.shields.io/badge/arXiv-xxxx.xxxx-b31b1b?style=for-the-badge&logo=arxiv&logoColor=b31b1b)]()
48
- [![License](https://img.shields.io/badge/License-MIT-green?style=for-the-badge)]()
49
- <!-- [![Gradio Demo](https://img.shields.io/badge/Gradio-Online_Demo-FFCC00?style=for-the-badge&logo=gradio&logoColor=yellow&labelColor=grey)]()
50
- [![Evaluation Split](https://img.shields.io/badge/HF-Test_Dataset-AECBFA?style=for-the-badge&logo=huggingface&logoColor=FFCC00&labelColor=grey)]() -->
51
-
52
- </div>
53
-
54
- ## 🔥 News
55
- > TODO
56
- - **[27 Sep 2025]** ⛳ Our preprint is now live on [arXiv] — check it out for details.
57
-
58
-
59
- ## Overview
60
- > TODO
61
-
62
-
63
- ## 📖 Contents
64
- - [⛏️ Installation](#️-installation)
65
- - [🔑 API Key Configuration](#-api-key-configuration)
66
- - [⚡ Quick Start](#-quick-start)
67
- - [CLI Inference](#cli-inference)
68
- - [Script Inference](#script-inference)
69
- - [Web Interface](#web-interface)
70
- - [📊 Evaluation](#-evaluation)
71
- - [📝 Citation](#-citation)
72
- - [📚 Acknowledgments](#-acknowledgments)
73
- - [📦 Codebase Contributors](#-codebase-contributors)
74
- - [📜 License](#-license)
75
-
76
- ## ⛏️ Installation
77
-
78
- > [!TIP]
79
- > Use [`uv`](https://pypi.org/project/uv) for installation — it's faster and more reliable than `pip`.
80
- ### For Development
81
-
82
- ```Shell
83
- # Create and activate a conda environment
84
- conda create -n EvoSci python=3.11 -y
85
- conda activate EvoSci
86
-
87
- # Install in development (editable) mode
88
- pip install EvoScientist
89
- # or
90
- pip install -e .
91
- ```
92
-
93
- ### Option 1:
94
- Install the latest version directly from GitHub for quick setup:
95
- > TODO
96
- ### Option 2:
97
- If you plan to modify the code or contribute to the project, you can clone the repository and install it in editable mode:
98
-
99
- > TODO
100
-
101
- <details>
102
- <summary> 🔄 Upgrade to the latest code base </summary>
103
-
104
- ```Shell
105
- git pull
106
- uv pip install -e .
107
- ```
108
-
109
- </details>
110
-
111
- ## 🔑 API Key Configuration
112
-
113
- EvoScientist requires API keys for LLM inference and web search. You can configure them in two ways:
114
-
115
- ### Option A: Environment Variables (Global)
116
-
117
- Set keys directly in your terminal session. Add these to your shell profile (`~/.bashrc`, `~/.zshrc`, etc.) to persist across sessions:
118
-
119
- ```Shell
120
- export ANTHROPIC_API_KEY="your_anthropic_api_key_here"
121
- export TAVILY_API_KEY="your_tavily_api_key_here"
122
- ```
123
-
124
- ### Option B: `.env` File (Project-level)
125
-
126
- Create a `.env` file in the project root. This keeps keys scoped to the project and out of your shell history:
127
-
128
- ```Shell
129
- cp .env.example .env
130
- ```
131
-
132
- Then edit `.env` and fill in your keys:
133
-
134
- ```
135
- ANTHROPIC_API_KEY=your_anthropic_api_key_here
136
- TAVILY_API_KEY=your_tavily_api_key_here
137
- ```
138
-
139
- > [!WARNING]
140
- > Never commit `.env` files containing real API keys to version control. The `.env` file is already included in `.gitignore`.
141
-
142
- | Key | Required | Description |
143
- |-----|----------|-------------|
144
- | `ANTHROPIC_API_KEY` | Yes | Anthropic API key for Claude ([console.anthropic.com](https://console.anthropic.com/)) |
145
- | `TAVILY_API_KEY` | Yes | Tavily API key for web search ([app.tavily.com](https://app.tavily.com/)) |
146
-
147
- ## ⚡ Quick Start
148
-
149
- ### CLI Inference
150
- You can perform inference directly from the command line using our CLI tool:
151
-
152
- ![demo](./assets/EvoScientist_cli.png)
153
-
154
- ```Shell
155
- python -m EvoScientist
156
- ```
157
- or
158
- ```Shell
159
- EvoSci # or EvoScientist
160
- ```
161
- **Optional arguments:**
162
-
163
- ```
164
- --workdir <path> Override workspace directory for this session
165
- --use-cwd Use current working directory as workspace
166
- --thread-id <id> Resume a conversation thread
167
- --no-thinking Disable thinking display
168
- ```
169
-
170
- **Interactive Commands:**
171
-
172
- | Command | Description |
173
- |---------|-------------|
174
- | `/exit` | Quit the session |
175
- | `/new` | Start a new session (new workspace + thread) |
176
- | `/thread` | Show current thread ID and workspace path |
177
- | `/skills` | List installed user skills |
178
- | `/install-skill <source>` | Install a skill from local path or GitHub |
179
- | `/uninstall-skill <name>` | Uninstall a user-installed skill |
180
-
181
- **Skill Installation Examples:**
182
-
183
- ```bash
184
- # Install from local path
185
- /install-skill ./my-skill
186
-
187
- # Install from GitHub URL
188
- /install-skill https://github.com/owner/repo/tree/main/skill-name
189
-
190
- # Install from GitHub shorthand
191
- /install-skill owner/repo@skill-name
192
- ```
193
-
194
- ### Runtime Directories
195
-
196
- By default, the **workspace** is created under a hidden directory in the current
197
- project directory:
198
-
199
- ```
200
- ./.evoscientist/workspace/
201
- memory/ # shared MEMORY.md
202
- skills/ # user-installed skills
203
- runs/ # per-thread workspaces
204
- ```
205
-
206
- You can force workspace to be the current directory via `--use-cwd`.
207
-
208
- If you set `EVOSCIENTIST_HOME`, EvoScientist will place `workspace/` under that
209
- directory instead of the project root:
210
-
211
- Example with `EVOSCIENTIST_HOME=~/.evoscientist`:
212
-
213
- ```
214
- ~/.evoscientist/
215
- workspace/
216
- memory/
217
- skills/
218
- runs/
219
- ```
220
-
221
- ### Script Inference
222
- ```python
223
- from EvoScientist import EvoScientist_agent
224
- from langchain_core.messages import HumanMessage
225
- from EvoScientist.utils import format_messages
226
-
227
- thread = {"configurable": {"thread_id": "1"}}
228
- question = "Hi?"
229
- last_len = 0
230
-
231
- for state in EvoScientist_agent.stream(
232
- {"messages": [HumanMessage(content=question)]},
233
- config=thread,
234
- stream_mode="values",
235
- ):
236
- msgs = state["messages"]
237
- if len(msgs) > last_len:
238
- format_messages(msgs[last_len:])
239
- last_len = len(msgs)
240
- ```
241
-
242
- <details>
243
- <summary> Output </summary>
244
-
245
- ```json
246
-
247
- ╭─────────────────────────────────────────────────── 🧑 Human ────────────────────────────────────────────────────╮
248
- │ Hi? │
249
- ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
250
- ╭───────────────────────────────────────────────────── 📝 AI ─────────────────────────────────────────────────────╮
251
- │ Hi! I'm here to help you with experimental research tasks. I can assist with: │
252
- │ │
253
- │ - **Planning experiments** - designing stages, success criteria, and workflows │
254
- │ - **Running experiments** - implementing baselines, training models, analyzing results │
255
- │ - **Research** - finding papers, methods, datasets, and baselines │
256
- │ - **Analysis** - computing metrics, creating visualizations, interpreting results │
257
- │ - **Writing** - drafting experimental reports and documentation │
258
- │ │
259
- │ What would you like to work on today? │
260
- ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
261
- ```
262
-
263
- </details>
264
-
265
- ### Web Interface
266
-
267
- > TODO
268
-
269
-
270
- ## 📊 Evaluation
271
-
272
- > TODO
273
-
274
- ## 📝 Citation
275
-
276
- If you find our paper and code useful in your research and applications, please cite using this BibTeX:
277
-
278
- > TODO
279
-
280
- ## 📚 Acknowledgments
281
-
282
- This project builds upon the following outstanding open-source works:
283
-
284
- - [**Deep Agents**](https://github.com/langchain-ai/deepagents) — A framework for building AI agents that can interact with various tools and environments.
285
- - [**Deep Agents UI**](https://github.com/langchain-ai/deep-agents-ui) — A user interface for visualising and managing Deep Agents.
286
-
287
- We thank the authors for their valuable contributions to the open-source community.
288
-
289
-
290
- ## 📦 Codebase Contributors
291
-
292
- <table>
293
- <tbody>
294
- <tr>
295
- <td align="center">
296
- <a href="https://youganglyu.github.io/">
297
- <img src="https://youganglyu.github.io/images/profile.png"
298
- width="100" height="100"
299
- style="object-fit: cover; border-radius: 20%;" alt="Yougang Lyu"/>
300
- <br />
301
- <sub><b>Yougang Lyu</b></sub>
302
- </a>
303
- </td>
304
- <td align="center">
305
- <a href="https://x-izhang.github.io/">
306
- <img src="https://x-izhang.github.io/author/xi-zhang/avatar_hu13660783057866068725.jpg"
307
- width="100" height="100"
308
- style="object-fit: cover; border-radius: 20%;" alt="Xi Zhang"/>
309
- <br />
310
- <sub><b>Xi Zhang</b></sub>
311
- </a>
312
- </td>
313
- </tr>
314
- </tbody>
315
- </table>
316
-
317
- For any enquiries or collaboration opportunities, please contact: [**youganglyu@gmail.com**](mailto:youganglyu@gmail.com)
318
-
319
- ## 📜 License
320
-
321
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.