EvoScientist 0.0.1.dev4__tar.gz → 0.0.1.dev5__tar.gz
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-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/EvoScientist.py +85 -32
- evoscientist-0.0.1.dev5/EvoScientist/__init__.py +60 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/backends.py +39 -32
- evoscientist-0.0.1.dev5/EvoScientist/channels/__init__.py +9 -0
- evoscientist-0.0.1.dev5/EvoScientist/channels/base.py +110 -0
- evoscientist-0.0.1.dev5/EvoScientist/channels/imessage/__init__.py +33 -0
- evoscientist-0.0.1.dev5/EvoScientist/channels/imessage/channel_rpc.py +427 -0
- evoscientist-0.0.1.dev5/EvoScientist/channels/imessage/probe.py +106 -0
- evoscientist-0.0.1.dev5/EvoScientist/channels/imessage/rpc_client.py +235 -0
- evoscientist-0.0.1.dev5/EvoScientist/channels/imessage/serve.py +428 -0
- evoscientist-0.0.1.dev5/EvoScientist/channels/imessage/targets.py +232 -0
- evoscientist-0.0.1.dev5/EvoScientist/cli.py +1653 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/config.py +11 -1
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/llm/models.py +20 -4
- evoscientist-0.0.1.dev5/EvoScientist/mcp/__init__.py +29 -0
- evoscientist-0.0.1.dev5/EvoScientist/mcp/client.py +591 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/middleware.py +7 -21
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/onboard.py +603 -3
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/paths.py +16 -0
- evoscientist-0.0.1.dev5/EvoScientist/skills/find-skills/SKILL.md +76 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/skills/skill-creator/SKILL.md +64 -58
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/stream/__init__.py +2 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/stream/display.py +209 -5
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/stream/formatter.py +4 -1
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/stream/utils.py +11 -2
- evoscientist-0.0.1.dev5/EvoScientist/tools/__init__.py +18 -0
- evoscientist-0.0.1.dev5/EvoScientist/tools/image.py +74 -0
- evoscientist-0.0.1.dev5/EvoScientist/tools/search.py +115 -0
- evoscientist-0.0.1.dev5/EvoScientist/tools/skill_manager.py +120 -0
- {evoscientist-0.0.1.dev4/EvoScientist → evoscientist-0.0.1.dev5/EvoScientist/tools}/skills_manager.py +105 -26
- evoscientist-0.0.1.dev5/EvoScientist/tools/think.py +32 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist.egg-info/PKG-INFO +162 -7
- evoscientist-0.0.1.dev5/EvoScientist.egg-info/SOURCES.txt +70 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist.egg-info/requires.txt +3 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/PKG-INFO +162 -7
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/README.md +158 -6
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/pyproject.toml +4 -1
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/tests/test_backends.py +88 -0
- evoscientist-0.0.1.dev5/tests/test_cli_run_name.py +38 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/tests/test_config.py +19 -0
- evoscientist-0.0.1.dev5/tests/test_event_loop.py +200 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/tests/test_imports.py +0 -15
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/tests/test_llm.py +20 -4
- evoscientist-0.0.1.dev5/tests/test_mcp_client.py +539 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/tests/test_onboard.py +294 -14
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/tests/test_skills_manager.py +7 -7
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/tests/test_stream_state.py +161 -11
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/tests/test_stream_utils.py +28 -4
- evoscientist-0.0.1.dev5/tests/test_tools.py +107 -0
- evoscientist-0.0.1.dev4/EvoScientist/__init__.py +0 -43
- evoscientist-0.0.1.dev4/EvoScientist/cli.py +0 -668
- evoscientist-0.0.1.dev4/EvoScientist/skills/accelerate/SKILL.md +0 -332
- evoscientist-0.0.1.dev4/EvoScientist/skills/accelerate/references/custom-plugins.md +0 -453
- evoscientist-0.0.1.dev4/EvoScientist/skills/accelerate/references/megatron-integration.md +0 -489
- evoscientist-0.0.1.dev4/EvoScientist/skills/accelerate/references/performance.md +0 -525
- evoscientist-0.0.1.dev4/EvoScientist/skills/bitsandbytes/SKILL.md +0 -411
- evoscientist-0.0.1.dev4/EvoScientist/skills/bitsandbytes/references/memory-optimization.md +0 -521
- evoscientist-0.0.1.dev4/EvoScientist/skills/bitsandbytes/references/qlora-training.md +0 -521
- evoscientist-0.0.1.dev4/EvoScientist/skills/bitsandbytes/references/quantization-formats.md +0 -447
- evoscientist-0.0.1.dev4/EvoScientist/skills/find-skills/SKILL.md +0 -133
- evoscientist-0.0.1.dev4/EvoScientist/skills/find-skills/scripts/install_skill.py +0 -211
- evoscientist-0.0.1.dev4/EvoScientist/skills/flash-attention/SKILL.md +0 -367
- evoscientist-0.0.1.dev4/EvoScientist/skills/flash-attention/references/benchmarks.md +0 -215
- evoscientist-0.0.1.dev4/EvoScientist/skills/flash-attention/references/transformers-integration.md +0 -293
- evoscientist-0.0.1.dev4/EvoScientist/skills/llama-cpp/SKILL.md +0 -258
- evoscientist-0.0.1.dev4/EvoScientist/skills/llama-cpp/references/optimization.md +0 -89
- evoscientist-0.0.1.dev4/EvoScientist/skills/llama-cpp/references/quantization.md +0 -213
- evoscientist-0.0.1.dev4/EvoScientist/skills/llama-cpp/references/server.md +0 -125
- evoscientist-0.0.1.dev4/EvoScientist/skills/lm-evaluation-harness/SKILL.md +0 -490
- evoscientist-0.0.1.dev4/EvoScientist/skills/lm-evaluation-harness/references/api-evaluation.md +0 -490
- evoscientist-0.0.1.dev4/EvoScientist/skills/lm-evaluation-harness/references/benchmark-guide.md +0 -488
- evoscientist-0.0.1.dev4/EvoScientist/skills/lm-evaluation-harness/references/custom-tasks.md +0 -602
- evoscientist-0.0.1.dev4/EvoScientist/skills/lm-evaluation-harness/references/distributed-eval.md +0 -519
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/SKILL.md +0 -937
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/references/checklists.md +0 -361
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/references/citation-workflow.md +0 -562
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/references/reviewer-guidelines.md +0 -367
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/references/sources.md +0 -159
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/references/writing-guide.md +0 -476
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/README.md +0 -251
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/aaai2026/README.md +0 -534
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026-unified-supp.tex +0 -144
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026-unified-template.tex +0 -952
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026.bib +0 -111
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026.bst +0 -1493
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026.sty +0 -315
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/acl/README.md +0 -50
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/acl/acl.sty +0 -312
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/acl/acl_latex.tex +0 -377
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/acl/acl_lualatex.tex +0 -101
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/acl/acl_natbib.bst +0 -1940
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/acl/anthology.bib.txt +0 -26
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/acl/custom.bib +0 -70
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/acl/formatting.md +0 -326
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/colm2025/README.md +0 -3
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.bib +0 -11
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.bst +0 -1440
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.pdf +0 -0
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.sty +0 -218
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.tex +0 -305
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/colm2025/fancyhdr.sty +0 -485
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/colm2025/math_commands.tex +0 -508
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/colm2025/natbib.sty +0 -1246
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/iclr2026/fancyhdr.sty +0 -485
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.bib +0 -24
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.bst +0 -1440
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.pdf +0 -0
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.sty +0 -246
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.tex +0 -414
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/iclr2026/math_commands.tex +0 -508
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/iclr2026/natbib.sty +0 -1246
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/icml2026/algorithm.sty +0 -79
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/icml2026/algorithmic.sty +0 -201
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/icml2026/example_paper.bib +0 -75
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/icml2026/example_paper.pdf +0 -0
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/icml2026/example_paper.tex +0 -662
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/icml2026/fancyhdr.sty +0 -864
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/icml2026/icml2026.bst +0 -1443
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/icml2026/icml2026.sty +0 -767
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/icml2026/icml_numpapers.pdf +0 -0
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/neurips2025/Makefile +0 -36
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/neurips2025/extra_pkgs.tex +0 -53
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/neurips2025/main.tex +0 -38
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/neurips2025/neurips.sty +0 -382
- evoscientist-0.0.1.dev4/EvoScientist/skills/peft/SKILL.md +0 -431
- evoscientist-0.0.1.dev4/EvoScientist/skills/peft/references/advanced-usage.md +0 -514
- evoscientist-0.0.1.dev4/EvoScientist/skills/peft/references/troubleshooting.md +0 -480
- evoscientist-0.0.1.dev4/EvoScientist/skills/ray-data/SKILL.md +0 -326
- evoscientist-0.0.1.dev4/EvoScientist/skills/ray-data/references/integration.md +0 -82
- evoscientist-0.0.1.dev4/EvoScientist/skills/ray-data/references/transformations.md +0 -83
- evoscientist-0.0.1.dev4/EvoScientist/skills/skill-creator/LICENSE.txt +0 -202
- evoscientist-0.0.1.dev4/EvoScientist/tools.py +0 -208
- evoscientist-0.0.1.dev4/EvoScientist.egg-info/SOURCES.txt +0 -132
- evoscientist-0.0.1.dev4/tests/test_tools.py +0 -18
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/__main__.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/llm/__init__.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/memory.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/prompts.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/skills/skill-creator/references/output-patterns.md +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/skills/skill-creator/references/workflows.md +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/skills/skill-creator/scripts/init_skill.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/skills/skill-creator/scripts/package_skill.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/skills/skill-creator/scripts/quick_validate.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/stream/emitter.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/stream/events.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/stream/state.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/stream/tracker.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/subagent.yaml +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist/utils.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist.egg-info/dependency_links.txt +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist.egg-info/entry_points.txt +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/EvoScientist.egg-info/top_level.txt +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/LICENSE +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/setup.cfg +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/tests/test_prompts.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/tests/test_stream_emitter.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev5}/tests/test_stream_tracker.py +0 -0
|
@@ -22,13 +22,15 @@ from deepagents.backends import FilesystemBackend, CompositeBackend
|
|
|
22
22
|
from .backends import CustomSandboxBackend, MergedReadOnlyBackend
|
|
23
23
|
from .config import get_effective_config, apply_config_to_env
|
|
24
24
|
from .llm import get_chat_model
|
|
25
|
+
from .mcp import load_mcp_tools
|
|
25
26
|
from .middleware import create_skills_middleware, create_memory_middleware
|
|
26
27
|
from .prompts import RESEARCHER_INSTRUCTIONS, get_system_prompt
|
|
27
28
|
from .utils import load_subagents
|
|
28
|
-
from .tools import tavily_search, think_tool, skill_manager
|
|
29
|
+
from .tools import tavily_search, think_tool, skill_manager, view_image
|
|
29
30
|
from .paths import (
|
|
30
31
|
ensure_dirs,
|
|
31
32
|
default_workspace_dir,
|
|
33
|
+
set_active_workspace,
|
|
32
34
|
MEMORY_DIR as _MEMORY_DIR_PATH,
|
|
33
35
|
USER_SKILLS_DIR as _USER_SKILLS_DIR_PATH,
|
|
34
36
|
)
|
|
@@ -51,6 +53,7 @@ MAX_ITERATIONS = _config.max_iterations
|
|
|
51
53
|
# Workspace settings
|
|
52
54
|
ensure_dirs()
|
|
53
55
|
WORKSPACE_DIR = str(default_workspace_dir())
|
|
56
|
+
set_active_workspace(WORKSPACE_DIR)
|
|
54
57
|
MEMORY_DIR = str(_MEMORY_DIR_PATH) # Shared across sessions (not per-session)
|
|
55
58
|
SKILLS_DIR = str(Path(__file__).parent / "skills")
|
|
56
59
|
USER_SKILLS_DIR = str(_USER_SKILLS_DIR_PATH)
|
|
@@ -73,7 +76,6 @@ SYSTEM_PROMPT = get_system_prompt(
|
|
|
73
76
|
chat_model = get_chat_model(
|
|
74
77
|
model=_config.model,
|
|
75
78
|
provider=_config.provider,
|
|
76
|
-
# thinking={"type": "enabled", "budget_tokens": 2000},
|
|
77
79
|
)
|
|
78
80
|
|
|
79
81
|
# Initialize workspace backend based on mode
|
|
@@ -113,33 +115,84 @@ backend = CompositeBackend(
|
|
|
113
115
|
tool_registry = {
|
|
114
116
|
"think_tool": think_tool,
|
|
115
117
|
"tavily_search": tavily_search,
|
|
118
|
+
"view_image": view_image,
|
|
116
119
|
}
|
|
117
120
|
|
|
121
|
+
# Base tools that every agent variant gets (before MCP)
|
|
122
|
+
BASE_TOOLS = [think_tool, skill_manager, view_image]
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _build_base_kwargs(base_backend, base_middleware):
|
|
126
|
+
"""Build agent kwargs *without* MCP (fast, no subprocess spawning)."""
|
|
127
|
+
subs = load_subagents(
|
|
128
|
+
SUBAGENTS_CONFIG,
|
|
129
|
+
tool_registry=tool_registry,
|
|
130
|
+
prompt_refs=prompt_refs,
|
|
131
|
+
)
|
|
132
|
+
return dict(
|
|
133
|
+
name="EvoScientist",
|
|
134
|
+
model=chat_model,
|
|
135
|
+
tools=list(BASE_TOOLS),
|
|
136
|
+
backend=base_backend,
|
|
137
|
+
subagents=subs,
|
|
138
|
+
middleware=base_middleware,
|
|
139
|
+
system_prompt=SYSTEM_PROMPT,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def load_mcp_and_build_kwargs(base_backend, base_middleware):
|
|
144
|
+
"""(Re-)load MCP tools and build agent kwargs.
|
|
145
|
+
|
|
146
|
+
Called on every ``create_cli_agent()`` call so that ``/new`` picks up
|
|
147
|
+
MCP config changes. Falls back to base kwargs if no MCP configured.
|
|
148
|
+
"""
|
|
149
|
+
mcp_by_agent = load_mcp_tools()
|
|
150
|
+
if not mcp_by_agent:
|
|
151
|
+
return _build_base_kwargs(base_backend, base_middleware)
|
|
152
|
+
|
|
153
|
+
# Fresh tool registry — start from base tools + MCP tools
|
|
154
|
+
registry = dict(tool_registry)
|
|
155
|
+
for tools in mcp_by_agent.values():
|
|
156
|
+
for t in tools:
|
|
157
|
+
registry[t.name] = t
|
|
158
|
+
|
|
159
|
+
mcp_main = mcp_by_agent.pop("main", [])
|
|
160
|
+
|
|
161
|
+
subs = load_subagents(
|
|
162
|
+
SUBAGENTS_CONFIG,
|
|
163
|
+
tool_registry=registry,
|
|
164
|
+
prompt_refs=prompt_refs,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# Inject MCP tools into subagents by name
|
|
168
|
+
for sa in subs:
|
|
169
|
+
if sa_tools := mcp_by_agent.get(sa["name"], []):
|
|
170
|
+
sa.setdefault("tools", []).extend(sa_tools)
|
|
171
|
+
|
|
172
|
+
return dict(
|
|
173
|
+
name="EvoScientist",
|
|
174
|
+
model=chat_model,
|
|
175
|
+
tools=BASE_TOOLS + mcp_main,
|
|
176
|
+
backend=base_backend,
|
|
177
|
+
subagents=subs,
|
|
178
|
+
middleware=base_middleware,
|
|
179
|
+
system_prompt=SYSTEM_PROMPT,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
118
183
|
prompt_refs = {
|
|
119
184
|
"RESEARCHER_INSTRUCTIONS": RESEARCHER_INSTRUCTIONS.format(date=current_date),
|
|
120
185
|
}
|
|
121
186
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
# Shared kwargs for agent creation
|
|
129
|
-
_AGENT_KWARGS = dict(
|
|
130
|
-
name="EvoScientist",
|
|
131
|
-
model=chat_model,
|
|
132
|
-
tools=[think_tool, skill_manager],
|
|
133
|
-
backend=backend,
|
|
134
|
-
subagents=subagents,
|
|
135
|
-
middleware=[
|
|
136
|
-
create_memory_middleware(MEMORY_DIR, extraction_model=chat_model),
|
|
137
|
-
create_skills_middleware(SKILLS_DIR, user_skills_dir=USER_SKILLS_DIR),
|
|
138
|
-
],
|
|
139
|
-
system_prompt=SYSTEM_PROMPT,
|
|
140
|
-
)
|
|
187
|
+
base_middleware = [
|
|
188
|
+
create_memory_middleware(MEMORY_DIR, extraction_model=chat_model),
|
|
189
|
+
create_skills_middleware(backend),
|
|
190
|
+
]
|
|
141
191
|
|
|
142
|
-
# Default agent (no checkpointer) — used by langgraph dev / LangSmith / notebooks
|
|
192
|
+
# Default agent (no checkpointer) — used by langgraph dev / LangSmith / notebooks.
|
|
193
|
+
# Built WITHOUT MCP at import time to avoid spawning subprocesses on every import.
|
|
194
|
+
# MCP tools are loaded on-demand in create_cli_agent().
|
|
195
|
+
_AGENT_KWARGS = _build_base_kwargs(backend, base_middleware)
|
|
143
196
|
EvoScientist_agent = create_deep_agent(**_AGENT_KWARGS).with_config({"recursion_limit": 500})
|
|
144
197
|
|
|
145
198
|
|
|
@@ -154,6 +207,7 @@ def create_cli_agent(workspace_dir: str | None = None):
|
|
|
154
207
|
from langgraph.checkpoint.memory import InMemorySaver # type: ignore[import-untyped]
|
|
155
208
|
|
|
156
209
|
if workspace_dir:
|
|
210
|
+
set_active_workspace(workspace_dir)
|
|
157
211
|
ws_backend = CustomSandboxBackend(
|
|
158
212
|
root_dir=workspace_dir,
|
|
159
213
|
virtual_mode=True,
|
|
@@ -175,17 +229,16 @@ def create_cli_agent(workspace_dir: str | None = None):
|
|
|
175
229
|
"/memory/": mem_backend,
|
|
176
230
|
},
|
|
177
231
|
)
|
|
178
|
-
mw = [
|
|
179
|
-
create_memory_middleware(MEMORY_DIR, extraction_model=chat_model),
|
|
180
|
-
create_skills_middleware(SKILLS_DIR, user_skills_dir=USER_SKILLS_DIR),
|
|
181
|
-
]
|
|
182
|
-
kwargs = dict(
|
|
183
|
-
_AGENT_KWARGS,
|
|
184
|
-
backend=be,
|
|
185
|
-
middleware=mw,
|
|
186
|
-
)
|
|
187
232
|
else:
|
|
188
|
-
|
|
233
|
+
be = backend
|
|
234
|
+
|
|
235
|
+
mw = [
|
|
236
|
+
create_memory_middleware(MEMORY_DIR, extraction_model=chat_model),
|
|
237
|
+
create_skills_middleware(be),
|
|
238
|
+
]
|
|
239
|
+
|
|
240
|
+
# Re-load MCP tools from current config (picks up /mcp add changes)
|
|
241
|
+
kwargs = load_mcp_and_build_kwargs(be, mw)
|
|
189
242
|
|
|
190
243
|
return create_deep_agent(
|
|
191
244
|
**kwargs,
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""EvoScientist Agent - AI-powered research and code execution.
|
|
2
|
+
|
|
3
|
+
This package exposes a convenience API at the package root while keeping
|
|
4
|
+
imports lazy, so lightweight modules (for example config helpers) can be used
|
|
5
|
+
without importing heavy runtime dependencies.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from importlib import import_module
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
_EXPORTS: dict[str, tuple[str, str]] = {
|
|
14
|
+
# Agent graph (lazy to avoid expensive initialization at import time)
|
|
15
|
+
"EvoScientist_agent": (".EvoScientist", "EvoScientist_agent"),
|
|
16
|
+
"create_cli_agent": (".EvoScientist", "create_cli_agent"),
|
|
17
|
+
# Backends
|
|
18
|
+
"CustomSandboxBackend": (".backends", "CustomSandboxBackend"),
|
|
19
|
+
"ReadOnlyFilesystemBackend": (".backends", "ReadOnlyFilesystemBackend"),
|
|
20
|
+
# Configuration
|
|
21
|
+
"EvoScientistConfig": (".config", "EvoScientistConfig"),
|
|
22
|
+
"load_config": (".config", "load_config"),
|
|
23
|
+
"save_config": (".config", "save_config"),
|
|
24
|
+
"get_effective_config": (".config", "get_effective_config"),
|
|
25
|
+
"get_config_path": (".config", "get_config_path"),
|
|
26
|
+
# LLM
|
|
27
|
+
"get_chat_model": (".llm", "get_chat_model"),
|
|
28
|
+
"MODELS": (".llm", "MODELS"),
|
|
29
|
+
"list_models": (".llm", "list_models"),
|
|
30
|
+
"DEFAULT_MODEL": (".llm", "DEFAULT_MODEL"),
|
|
31
|
+
# Middleware
|
|
32
|
+
"create_skills_middleware": (".middleware", "create_skills_middleware"),
|
|
33
|
+
# Prompts
|
|
34
|
+
"get_system_prompt": (".prompts", "get_system_prompt"),
|
|
35
|
+
"RESEARCHER_INSTRUCTIONS": (".prompts", "RESEARCHER_INSTRUCTIONS"),
|
|
36
|
+
# Tools
|
|
37
|
+
"tavily_search": (".tools", "tavily_search"),
|
|
38
|
+
"think_tool": (".tools", "think_tool"),
|
|
39
|
+
"view_image": (".tools", "view_image"),
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def __getattr__(name: str):
|
|
44
|
+
target = _EXPORTS.get(name)
|
|
45
|
+
if target is None:
|
|
46
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
47
|
+
|
|
48
|
+
module_name, attr_name = target
|
|
49
|
+
module = import_module(module_name, package=__name__)
|
|
50
|
+
value = getattr(module, attr_name)
|
|
51
|
+
# Cache after first load to avoid repeated import lookups.
|
|
52
|
+
globals()[name] = value
|
|
53
|
+
return value
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def __dir__() -> list[str]:
|
|
57
|
+
return sorted(set(globals()) | set(_EXPORTS))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
__all__ = list(_EXPORTS)
|
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
import os
|
|
4
4
|
import re
|
|
5
5
|
import subprocess
|
|
6
|
+
import uuid
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
|
|
8
9
|
from deepagents.backends import FilesystemBackend
|
|
9
10
|
from deepagents.backends.filesystem import WriteResult, EditResult
|
|
10
11
|
from deepagents.backends.protocol import (
|
|
12
|
+
BackendProtocol,
|
|
11
13
|
ExecuteResponse,
|
|
12
14
|
FileDownloadResponse,
|
|
13
15
|
FileUploadResponse,
|
|
@@ -132,7 +134,7 @@ class ReadOnlyFilesystemBackend(FilesystemBackend):
|
|
|
132
134
|
)
|
|
133
135
|
|
|
134
136
|
|
|
135
|
-
class MergedReadOnlyBackend:
|
|
137
|
+
class MergedReadOnlyBackend(BackendProtocol):
|
|
136
138
|
"""Read-only backend that merges two directories.
|
|
137
139
|
|
|
138
140
|
Reads from *primary* first (user skills in workspace/skills/),
|
|
@@ -205,27 +207,7 @@ class MergedReadOnlyBackend:
|
|
|
205
207
|
error="This directory is read-only. Edit operations are not permitted here."
|
|
206
208
|
)
|
|
207
209
|
|
|
208
|
-
# --
|
|
209
|
-
|
|
210
|
-
async def aread(self, file_path: str, offset: int = 0, limit: int = 2000) -> str:
|
|
211
|
-
return self.read(file_path, offset, limit)
|
|
212
|
-
|
|
213
|
-
async def als_info(self, path: str = "/") -> list:
|
|
214
|
-
return self.ls_info(path)
|
|
215
|
-
|
|
216
|
-
async def agrep_raw(self, pattern: str, path: str | None = None, glob: str | None = None) -> list:
|
|
217
|
-
return self.grep_raw(pattern, path, glob)
|
|
218
|
-
|
|
219
|
-
async def aglob_info(self, pattern: str, path: str = "/") -> list:
|
|
220
|
-
return self.glob_info(pattern, path)
|
|
221
|
-
|
|
222
|
-
async def awrite(self, file_path: str, content: str) -> WriteResult:
|
|
223
|
-
return self.write(file_path, content)
|
|
224
|
-
|
|
225
|
-
async def aedit(self, file_path: str, old_string: str, new_string: str, replace_all: bool = False) -> EditResult:
|
|
226
|
-
return self.edit(file_path, old_string, new_string, replace_all)
|
|
227
|
-
|
|
228
|
-
# -- download / upload (required by BackendProtocol) --
|
|
210
|
+
# -- download / upload --
|
|
229
211
|
|
|
230
212
|
def download_files(self, paths: list[str]) -> list[FileDownloadResponse]:
|
|
231
213
|
"""Download files, trying primary then secondary."""
|
|
@@ -237,18 +219,12 @@ class MergedReadOnlyBackend:
|
|
|
237
219
|
responses.append(resp)
|
|
238
220
|
return responses
|
|
239
221
|
|
|
240
|
-
async def adownload_files(self, paths: list[str]) -> list[FileDownloadResponse]:
|
|
241
|
-
return self.download_files(paths)
|
|
242
|
-
|
|
243
222
|
def upload_files(self, files: list[tuple[str, bytes]]) -> list[FileUploadResponse]:
|
|
244
223
|
return [
|
|
245
224
|
FileUploadResponse(path=path, error="permission_denied")
|
|
246
225
|
for path, _ in files
|
|
247
226
|
]
|
|
248
227
|
|
|
249
|
-
async def aupload_files(self, files: list[tuple[str, bytes]]) -> list[FileUploadResponse]:
|
|
250
|
-
return self.upload_files(files)
|
|
251
|
-
|
|
252
228
|
|
|
253
229
|
class CustomSandboxBackend(FilesystemBackend, SandboxBackendProtocol):
|
|
254
230
|
"""
|
|
@@ -269,6 +245,9 @@ class CustomSandboxBackend(FilesystemBackend, SandboxBackendProtocol):
|
|
|
269
245
|
working_dir: str | None = None,
|
|
270
246
|
timeout: int = 300,
|
|
271
247
|
shell: str = "/bin/bash",
|
|
248
|
+
max_output_bytes: int = 100_000,
|
|
249
|
+
env: dict[str, str] | None = None,
|
|
250
|
+
inherit_env: bool = True,
|
|
272
251
|
):
|
|
273
252
|
"""
|
|
274
253
|
Initialize custom sandbox backend.
|
|
@@ -279,17 +258,33 @@ class CustomSandboxBackend(FilesystemBackend, SandboxBackendProtocol):
|
|
|
279
258
|
working_dir: Working directory for command execution (defaults to root_dir)
|
|
280
259
|
timeout: Command execution timeout in seconds
|
|
281
260
|
shell: Shell program to use
|
|
261
|
+
max_output_bytes: Max output size before truncation (default 100KB)
|
|
262
|
+
env: Extra environment variables for subprocess
|
|
263
|
+
inherit_env: Whether to inherit parent process env (default True)
|
|
282
264
|
"""
|
|
283
265
|
super().__init__(root_dir=root_dir, virtual_mode=virtual_mode)
|
|
284
266
|
|
|
267
|
+
self._sandbox_id = f"evosci-{uuid.uuid4().hex[:8]}"
|
|
285
268
|
self.working_dir = working_dir or root_dir
|
|
286
269
|
self.timeout = timeout
|
|
287
270
|
self.shell = shell
|
|
288
271
|
self.virtual_mode = virtual_mode
|
|
272
|
+
self._max_output_bytes = max_output_bytes
|
|
273
|
+
|
|
274
|
+
# Build subprocess environment
|
|
275
|
+
if inherit_env:
|
|
276
|
+
self._env = {**os.environ, **(env or {})}
|
|
277
|
+
else:
|
|
278
|
+
self._env = dict(env) if env else {}
|
|
289
279
|
|
|
290
280
|
# Ensure working directory exists
|
|
291
281
|
os.makedirs(self.working_dir, exist_ok=True)
|
|
292
282
|
|
|
283
|
+
@property
|
|
284
|
+
def id(self) -> str:
|
|
285
|
+
"""Unique identifier for the sandbox backend instance."""
|
|
286
|
+
return self._sandbox_id
|
|
287
|
+
|
|
293
288
|
def _resolve_path(self, key: str) -> Path:
|
|
294
289
|
"""Resolve path with sanitization to prevent nested directories.
|
|
295
290
|
|
|
@@ -359,18 +354,30 @@ class CustomSandboxBackend(FilesystemBackend, SandboxBackendProtocol):
|
|
|
359
354
|
capture_output=True,
|
|
360
355
|
text=True,
|
|
361
356
|
timeout=self.timeout,
|
|
357
|
+
env=self._env,
|
|
362
358
|
)
|
|
363
359
|
|
|
364
|
-
|
|
360
|
+
output_parts = []
|
|
365
361
|
if result.stdout:
|
|
366
|
-
|
|
362
|
+
output_parts.append(result.stdout)
|
|
367
363
|
if result.stderr:
|
|
368
|
-
|
|
364
|
+
stderr_lines = result.stderr.strip().split("\n")
|
|
365
|
+
output_parts.extend(f"[stderr] {line}" for line in stderr_lines)
|
|
366
|
+
output = "\n".join(output_parts) if output_parts else ""
|
|
367
|
+
|
|
368
|
+
if result.returncode != 0:
|
|
369
|
+
output = f"{output.rstrip()}\n\nExit code: {result.returncode}"
|
|
370
|
+
|
|
371
|
+
truncated = False
|
|
372
|
+
if len(output) > self._max_output_bytes:
|
|
373
|
+
output = output[:self._max_output_bytes]
|
|
374
|
+
output += f"\n\n... Output truncated at {self._max_output_bytes} bytes."
|
|
375
|
+
truncated = True
|
|
369
376
|
|
|
370
377
|
return ExecuteResponse(
|
|
371
378
|
output=output,
|
|
372
379
|
exit_code=result.returncode,
|
|
373
|
-
truncated=
|
|
380
|
+
truncated=truncated,
|
|
374
381
|
)
|
|
375
382
|
|
|
376
383
|
except subprocess.TimeoutExpired:
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Communication channels for EvoScientist.
|
|
2
|
+
|
|
3
|
+
This module provides an extensible interface for different messaging channels
|
|
4
|
+
(iMessage, WeChat, etc.) to communicate with the EvoScientist agent.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .base import Channel, IncomingMessage, OutgoingMessage
|
|
8
|
+
|
|
9
|
+
__all__ = ["Channel", "IncomingMessage", "OutgoingMessage"]
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Abstract base class for communication channels.
|
|
2
|
+
|
|
3
|
+
This module defines the Channel interface that all messaging channels
|
|
4
|
+
(iMessage, WeChat, etc.) must implement.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from typing import AsyncIterator
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class IncomingMessage:
|
|
15
|
+
"""Represents a message received from a channel."""
|
|
16
|
+
|
|
17
|
+
sender: str # Phone number, email, or unique identifier
|
|
18
|
+
content: str # Message text content
|
|
19
|
+
timestamp: datetime # When the message was sent
|
|
20
|
+
message_id: str # Unique identifier for the message
|
|
21
|
+
metadata: dict = field(default_factory=dict) # Channel-specific metadata
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class OutgoingMessage:
|
|
26
|
+
"""Represents a message to be sent through a channel."""
|
|
27
|
+
|
|
28
|
+
recipient: str # Phone number, email, or unique identifier
|
|
29
|
+
content: str # Message text content
|
|
30
|
+
reply_to: str | None = None # Optional message ID being replied to
|
|
31
|
+
metadata: dict = field(default_factory=dict) # Channel-specific metadata
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Channel(ABC):
|
|
35
|
+
"""Abstract base class for messaging channels.
|
|
36
|
+
|
|
37
|
+
Subclasses must implement:
|
|
38
|
+
- start(): Initialize the channel (connect, authenticate, etc.)
|
|
39
|
+
- stop(): Clean up resources
|
|
40
|
+
- receive(): Async iterator yielding incoming messages
|
|
41
|
+
- send(): Send a message through the channel
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
@abstractmethod
|
|
45
|
+
async def start(self) -> None:
|
|
46
|
+
"""Initialize and start the channel.
|
|
47
|
+
|
|
48
|
+
This method should:
|
|
49
|
+
- Establish connections
|
|
50
|
+
- Verify permissions/authentication
|
|
51
|
+
- Start any background tasks needed
|
|
52
|
+
|
|
53
|
+
Raises:
|
|
54
|
+
ChannelError: If initialization fails
|
|
55
|
+
"""
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
@abstractmethod
|
|
59
|
+
async def stop(self) -> None:
|
|
60
|
+
"""Stop the channel and clean up resources.
|
|
61
|
+
|
|
62
|
+
This method should:
|
|
63
|
+
- Close connections
|
|
64
|
+
- Cancel background tasks
|
|
65
|
+
- Release any held resources
|
|
66
|
+
"""
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
@abstractmethod
|
|
70
|
+
async def receive(self) -> AsyncIterator[IncomingMessage]:
|
|
71
|
+
"""Async iterator that yields incoming messages.
|
|
72
|
+
|
|
73
|
+
Yields:
|
|
74
|
+
IncomingMessage: Each new message received
|
|
75
|
+
|
|
76
|
+
Example:
|
|
77
|
+
async for msg in channel.receive():
|
|
78
|
+
print(f"From {msg.sender}: {msg.content}")
|
|
79
|
+
"""
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
@abstractmethod
|
|
83
|
+
async def send(self, message: OutgoingMessage) -> bool:
|
|
84
|
+
"""Send a message through the channel.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
message: The message to send
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
True if sent successfully, False otherwise
|
|
91
|
+
"""
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class ChannelError(Exception):
|
|
96
|
+
"""Base exception for channel-related errors."""
|
|
97
|
+
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class ChannelPermissionError(ChannelError):
|
|
102
|
+
"""Raised when the channel lacks required permissions."""
|
|
103
|
+
|
|
104
|
+
pass
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class ChannelConnectionError(ChannelError):
|
|
108
|
+
"""Raised when the channel cannot establish a connection."""
|
|
109
|
+
|
|
110
|
+
pass
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""iMessage channel implementation for EvoScientist.
|
|
2
|
+
|
|
3
|
+
Uses imsg CLI via JSON-RPC for real-time message streaming.
|
|
4
|
+
|
|
5
|
+
Requirements:
|
|
6
|
+
- macOS only
|
|
7
|
+
- imsg CLI: brew install steipete/tap/imsg
|
|
8
|
+
- Full Disk Access permission
|
|
9
|
+
- Messages.app logged into iCloud
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from .channel_rpc import IMessageChannelRpc as IMessageChannel
|
|
13
|
+
from .channel_rpc import IMessageConfig
|
|
14
|
+
from .probe import probe_imessage, ProbeResult
|
|
15
|
+
from .targets import (
|
|
16
|
+
parse_target,
|
|
17
|
+
normalize_handle,
|
|
18
|
+
normalize_e164,
|
|
19
|
+
IMessageTarget,
|
|
20
|
+
IMessageService,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"IMessageChannel",
|
|
25
|
+
"IMessageConfig",
|
|
26
|
+
"probe_imessage",
|
|
27
|
+
"ProbeResult",
|
|
28
|
+
"parse_target",
|
|
29
|
+
"normalize_handle",
|
|
30
|
+
"normalize_e164",
|
|
31
|
+
"IMessageTarget",
|
|
32
|
+
"IMessageService",
|
|
33
|
+
]
|