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.
- EvoScientist/EvoScientist.py +17 -49
- EvoScientist/backends.py +0 -26
- EvoScientist/cli.py +1109 -255
- EvoScientist/middleware.py +8 -61
- EvoScientist/stream/__init__.py +0 -25
- EvoScientist/stream/utils.py +16 -23
- EvoScientist/tools.py +0 -64
- evoscientist-0.1.0rc1.dist-info/METADATA +199 -0
- evoscientist-0.1.0rc1.dist-info/RECORD +21 -0
- evoscientist-0.1.0rc1.dist-info/entry_points.txt +2 -0
- EvoScientist/memory.py +0 -715
- EvoScientist/paths.py +0 -45
- EvoScientist/skills/accelerate/SKILL.md +0 -332
- EvoScientist/skills/accelerate/references/custom-plugins.md +0 -453
- EvoScientist/skills/accelerate/references/megatron-integration.md +0 -489
- EvoScientist/skills/accelerate/references/performance.md +0 -525
- EvoScientist/skills/bitsandbytes/SKILL.md +0 -411
- EvoScientist/skills/bitsandbytes/references/memory-optimization.md +0 -521
- EvoScientist/skills/bitsandbytes/references/qlora-training.md +0 -521
- EvoScientist/skills/bitsandbytes/references/quantization-formats.md +0 -447
- EvoScientist/skills/find-skills/SKILL.md +0 -133
- EvoScientist/skills/find-skills/scripts/install_skill.py +0 -211
- EvoScientist/skills/flash-attention/SKILL.md +0 -367
- EvoScientist/skills/flash-attention/references/benchmarks.md +0 -215
- EvoScientist/skills/flash-attention/references/transformers-integration.md +0 -293
- EvoScientist/skills/llama-cpp/SKILL.md +0 -258
- EvoScientist/skills/llama-cpp/references/optimization.md +0 -89
- EvoScientist/skills/llama-cpp/references/quantization.md +0 -213
- EvoScientist/skills/llama-cpp/references/server.md +0 -125
- EvoScientist/skills/lm-evaluation-harness/SKILL.md +0 -490
- EvoScientist/skills/lm-evaluation-harness/references/api-evaluation.md +0 -490
- EvoScientist/skills/lm-evaluation-harness/references/benchmark-guide.md +0 -488
- EvoScientist/skills/lm-evaluation-harness/references/custom-tasks.md +0 -602
- EvoScientist/skills/lm-evaluation-harness/references/distributed-eval.md +0 -519
- EvoScientist/skills/ml-paper-writing/SKILL.md +0 -937
- EvoScientist/skills/ml-paper-writing/references/checklists.md +0 -361
- EvoScientist/skills/ml-paper-writing/references/citation-workflow.md +0 -562
- EvoScientist/skills/ml-paper-writing/references/reviewer-guidelines.md +0 -367
- EvoScientist/skills/ml-paper-writing/references/sources.md +0 -159
- EvoScientist/skills/ml-paper-writing/references/writing-guide.md +0 -476
- EvoScientist/skills/ml-paper-writing/templates/README.md +0 -251
- EvoScientist/skills/ml-paper-writing/templates/aaai2026/README.md +0 -534
- EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026-unified-supp.tex +0 -144
- EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026-unified-template.tex +0 -952
- EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026.bib +0 -111
- EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026.bst +0 -1493
- EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026.sty +0 -315
- EvoScientist/skills/ml-paper-writing/templates/acl/README.md +0 -50
- EvoScientist/skills/ml-paper-writing/templates/acl/acl.sty +0 -312
- EvoScientist/skills/ml-paper-writing/templates/acl/acl_latex.tex +0 -377
- EvoScientist/skills/ml-paper-writing/templates/acl/acl_lualatex.tex +0 -101
- EvoScientist/skills/ml-paper-writing/templates/acl/acl_natbib.bst +0 -1940
- EvoScientist/skills/ml-paper-writing/templates/acl/anthology.bib.txt +0 -26
- EvoScientist/skills/ml-paper-writing/templates/acl/custom.bib +0 -70
- EvoScientist/skills/ml-paper-writing/templates/acl/formatting.md +0 -326
- EvoScientist/skills/ml-paper-writing/templates/colm2025/README.md +0 -3
- EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.bib +0 -11
- EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.bst +0 -1440
- EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.pdf +0 -0
- EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.sty +0 -218
- EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.tex +0 -305
- EvoScientist/skills/ml-paper-writing/templates/colm2025/fancyhdr.sty +0 -485
- EvoScientist/skills/ml-paper-writing/templates/colm2025/math_commands.tex +0 -508
- EvoScientist/skills/ml-paper-writing/templates/colm2025/natbib.sty +0 -1246
- EvoScientist/skills/ml-paper-writing/templates/iclr2026/fancyhdr.sty +0 -485
- EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.bib +0 -24
- EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.bst +0 -1440
- EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.pdf +0 -0
- EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.sty +0 -246
- EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.tex +0 -414
- EvoScientist/skills/ml-paper-writing/templates/iclr2026/math_commands.tex +0 -508
- EvoScientist/skills/ml-paper-writing/templates/iclr2026/natbib.sty +0 -1246
- EvoScientist/skills/ml-paper-writing/templates/icml2026/algorithm.sty +0 -79
- EvoScientist/skills/ml-paper-writing/templates/icml2026/algorithmic.sty +0 -201
- EvoScientist/skills/ml-paper-writing/templates/icml2026/example_paper.bib +0 -75
- EvoScientist/skills/ml-paper-writing/templates/icml2026/example_paper.pdf +0 -0
- EvoScientist/skills/ml-paper-writing/templates/icml2026/example_paper.tex +0 -662
- EvoScientist/skills/ml-paper-writing/templates/icml2026/fancyhdr.sty +0 -864
- EvoScientist/skills/ml-paper-writing/templates/icml2026/icml2026.bst +0 -1443
- EvoScientist/skills/ml-paper-writing/templates/icml2026/icml2026.sty +0 -767
- EvoScientist/skills/ml-paper-writing/templates/icml2026/icml_numpapers.pdf +0 -0
- EvoScientist/skills/ml-paper-writing/templates/neurips2025/Makefile +0 -36
- EvoScientist/skills/ml-paper-writing/templates/neurips2025/extra_pkgs.tex +0 -53
- EvoScientist/skills/ml-paper-writing/templates/neurips2025/main.tex +0 -38
- EvoScientist/skills/ml-paper-writing/templates/neurips2025/neurips.sty +0 -382
- EvoScientist/skills/peft/SKILL.md +0 -431
- EvoScientist/skills/peft/references/advanced-usage.md +0 -514
- EvoScientist/skills/peft/references/troubleshooting.md +0 -480
- EvoScientist/skills/ray-data/SKILL.md +0 -326
- EvoScientist/skills/ray-data/references/integration.md +0 -82
- EvoScientist/skills/ray-data/references/transformations.md +0 -83
- EvoScientist/skills/skill-creator/LICENSE.txt +0 -202
- EvoScientist/skills/skill-creator/SKILL.md +0 -356
- EvoScientist/skills/skill-creator/references/output-patterns.md +0 -82
- EvoScientist/skills/skill-creator/references/workflows.md +0 -28
- EvoScientist/skills/skill-creator/scripts/init_skill.py +0 -303
- EvoScientist/skills/skill-creator/scripts/package_skill.py +0 -110
- EvoScientist/skills/skill-creator/scripts/quick_validate.py +0 -95
- EvoScientist/skills_manager.py +0 -392
- EvoScientist/stream/display.py +0 -604
- EvoScientist/stream/events.py +0 -415
- EvoScientist/stream/state.py +0 -343
- evoscientist-0.0.1.dev3.dist-info/METADATA +0 -321
- evoscientist-0.0.1.dev3.dist-info/RECORD +0 -113
- evoscientist-0.0.1.dev3.dist-info/entry_points.txt +0 -5
- {evoscientist-0.0.1.dev3.dist-info → evoscientist-0.1.0rc1.dist-info}/WHEEL +0 -0
- {evoscientist-0.0.1.dev3.dist-info → evoscientist-0.1.0rc1.dist-info}/licenses/LICENSE +0 -0
- {evoscientist-0.0.1.dev3.dist-info → evoscientist-0.1.0rc1.dist-info}/top_level.txt +0 -0
EvoScientist/stream/state.py
DELETED
|
@@ -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
|
-
[](https://pypi.org/project/EvoScientist/)
|
|
46
|
-
[]()
|
|
47
|
-
[]()
|
|
48
|
-
[]()
|
|
49
|
-
<!-- []()
|
|
50
|
-
[]() -->
|
|
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
|
-

|
|
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.
|