Files
m4 c683f6e739
Docker / build (push) Has been cancelled
Build / build (push) Has been cancelled
Lint / ruff (push) Has been cancelled
Test / pytest (ubuntu-latest, 3.11) (push) Has been cancelled
Test / pytest (ubuntu-latest, 3.12) (push) Has been cancelled
Test / pytest (windows-latest, 3.11) (push) Has been cancelled
Test / pytest (windows-latest, 3.12) (push) Has been cancelled
feat: prepare EvoScientist 0.3.0
Add bounded document ingestion, controlled web search, recoverable session support, subagent timeouts, and the native sandbox runtime contract. Unify package versioning and add release-focused regression coverage.
2026-09-03 06:55:56 +08:00

213 lines
7.7 KiB
Python

"""Tests for EvoScientist/prompts.py."""
from EvoScientist.prompts import (
DELEGATION_STRATEGY,
EVOSCIENTIST_IDENTITY,
EXPERIMENT_WORKFLOW,
REPORT_TEMPLATE,
SHELL_GUIDELINES,
WRITING_GUIDELINES,
get_system_prompt,
)
class TestGetSystemPrompt:
def test_returns_non_empty(self):
result = get_system_prompt()
assert isinstance(result, str)
assert len(result) > 100
def test_contains_identity(self):
result = get_system_prompt()
assert "EvoScientist" in result
assert "self-evolving" in result
def test_contains_workflow(self):
result = get_system_prompt()
assert "Experiment Workflow" in result
def test_contains_report_template(self):
result = get_system_prompt()
assert "Experiment Report Template" in result
def test_contains_writing_guidelines(self):
result = get_system_prompt()
assert "Writing Guidelines" in result
def test_contains_shell_guidelines(self):
result = get_system_prompt()
assert "Shell Execution Guidelines" in result
def test_contains_safe_archive_and_sqlite_programming_contracts(self):
result = get_system_prompt(native_web_sandbox=True)
assert "never use `extractall`" in result
assert "mode=ro&immutable=1" in result
assert "PRAGMA query_only=ON" in result
assert "Never execute files extracted from an archive" in result
assert "Do not modify original files under `uploads/`" in result
def test_distinguishes_live_web_search_from_local_memory_search(self):
result = get_system_prompt(native_web_sandbox=True)
assert "tavily_search" in result
assert "search_observations" in result
assert "local memory" in result.lower()
assert "Do not use `execute`, `curl`, or `httpx`" in result
def test_contains_delegation(self):
result = get_system_prompt()
assert "Sub-Agent Delegation" in result
def test_no_numeric_limits(self):
result = get_system_prompt()
assert "{max_concurrent}" not in result
assert "{max_iterations}" not in result
def test_workflow_constant_not_empty(self):
assert len(EXPERIMENT_WORKFLOW) > 0
def test_delegation_no_placeholders(self):
assert "{max_concurrent}" not in DELEGATION_STRATEGY
assert "{max_iterations}" not in DELEGATION_STRATEGY
def test_section_ordering(self):
"""Identity must precede workflow; workflow must precede delegation."""
result = get_system_prompt()
idx_identity = result.find("# Identity")
idx_workflow = result.find("# Experiment Workflow")
idx_delegation = result.find("# Sub-Agent Delegation")
assert 0 <= idx_identity < idx_workflow < idx_delegation
def test_does_not_contain_static_date(self):
"""Date is injected per-turn by runtime context, not baked into static prompt.
Static prompt must stay byte-stable across midnight so the cache prefix
survives. See RuntimeContextMiddleware for runtime injection.
"""
import re
result = get_system_prompt()
assert not re.search(r"Current date: \d{4}-\d{2}-\d{2}", result)
def test_mentions_skill_manager_for_discovery(self):
"""Agent must know it can browse/install skills from the EvoSkills catalog."""
result = get_system_prompt()
assert "skill_manager" in result
assert "EvoSkills" in result
def test_no_stale_memory_path_singular(self):
"""Backend route is `/memories/`, not `/memory/`. Catch silent-bug regressions.
Anything sent to `/memory/...` falls through to CustomSandboxBackend
(workspace files), bypassing the persistent FilesystemBackend that
owns persistent memory files.
"""
result = get_system_prompt()
# `/memory/` as a filesystem path (after a backtick or whitespace, before
# an alpha char or another /). Excludes word-list usages like
# "context/memory/web search".
import re
assert not re.search(r"[\s`]/memory/[a-zA-Z]", result), (
"Found `/memory/<file>` in system prompt — should be `/memories/<file>`"
)
class TestEvoScientistIdentity:
def test_constant_not_empty(self):
assert len(EVOSCIENTIST_IDENTITY) > 0
def test_states_role(self):
assert "You are EvoScientist" in EVOSCIENTIST_IDENTITY
def test_mentions_human_on_the_loop_paradigm(self):
# Behavioral cue: agent should know it isn't asking permission for every action
assert "on-the-loop" in EVOSCIENTIST_IDENTITY
class TestReportTemplate:
def test_constant_not_empty(self):
assert len(REPORT_TEMPLATE) > 0
def test_contains_six_sections(self):
# Match the six recommended sections (lowercased to be tolerant of phrasing)
body = REPORT_TEMPLATE.lower()
for section in (
"summary",
"experiment plan",
"setup",
"baselines",
"results",
"limitations",
):
assert section in body, section
def test_not_duplicated_in_workflow_step5(self):
"""Step 5 should reference REPORT_TEMPLATE, not redefine the schema."""
# Positive: Step 5 must actually reference the report template.
assert "Experiment Report Template" in EXPERIMENT_WORKFLOW
# Negative: section headers unique to REPORT_TEMPLATE must not appear
# inlined inside EXPERIMENT_WORKFLOW (would mean the schema was
# duplicated again, regardless of indentation style).
assert "Baselines and comparisons" not in EXPERIMENT_WORKFLOW
class TestWritingGuidelines:
def test_constant_not_empty(self):
assert len(WRITING_GUIDELINES) > 0
def test_mentions_first_person_avoidance(self):
assert (
"first-person" in WRITING_GUIDELINES.lower()
or "I ..." in WRITING_GUIDELINES
)
class TestShellGuidelines:
def test_constant_not_empty(self):
assert len(SHELL_GUIDELINES) > 0
def test_mentions_timeout_limit(self):
assert "300" in SHELL_GUIDELINES # default timeout
assert "3600" in SHELL_GUIDELINES # per-command override ceiling
def test_mentions_background_execution(self):
assert "background" in SHELL_GUIDELINES.lower()
def test_not_duplicated_in_workflow(self):
"""SHELL_GUIDELINES content should live ONLY in its own constant."""
# Sentinel phrase unique to SHELL_GUIDELINES
assert "Sandbox limits" not in EXPERIMENT_WORKFLOW
class TestDangerousShellGuidelines:
def test_default_uses_virtual_paths(self):
result = get_system_prompt()
assert "> /output.log" in result
assert "DANGEROUS MODE" not in result
def test_dangerous_swaps_guidelines(self):
result = get_system_prompt(dangerous=True, cwd="/Users/me/ws/demo")
assert "DANGEROUS MODE" in result
assert "/Users/me/ws/demo" in result
# virtual-path example is gone
assert "> /output.log" not in result
# privileged-command blocklist still advertised
assert "sudo" in result
assert "rm -rf /" in result
def test_dangerous_without_cwd_falls_back(self):
result = get_system_prompt(dangerous=True)
assert "DANGEROUS MODE" in result
class TestNativeWebShellGuidelines:
def test_uses_relative_foreground_contract(self):
result = get_system_prompt(native_web_sandbox=True)
assert "$WORKSPACE` is `.`" in result
assert "Do not use `/workspace` inside shell commands" in result
assert "Do not append `&`" in result
assert "run_in_background" not in result
assert "> /output.log" not in result