120 lines
5.9 KiB
Python
120 lines
5.9 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
import unittest
|
|
|
|
from agent_builder.builder import (
|
|
BUILDER_SKILL_SOURCE,
|
|
SYSTEM_PROMPT,
|
|
_builder_skill_files,
|
|
_with_builder_skills,
|
|
)
|
|
from agent_builder.tools import A2A_PACK_MIN_VERSION
|
|
from deepagents.backends import StateBackend
|
|
|
|
|
|
class BuilderPromptTests(unittest.TestCase):
|
|
def test_outer_agent_uses_500_recursion_limit(self) -> None:
|
|
agent_source = Path(__file__).resolve().parents[1].joinpath("agent.py").read_text()
|
|
|
|
self.assertIn("DEEPAGENTS_RECURSION_LIMIT = 500", agent_source)
|
|
self.assertIn(
|
|
"config={\"recursion_limit\": DEEPAGENTS_RECURSION_LIMIT}",
|
|
agent_source,
|
|
)
|
|
self.assertIn("stream=True", agent_source)
|
|
self.assertIn('grant_write_prefixes=("agents/{name}/",)', agent_source)
|
|
|
|
def test_outer_builder_accepts_user_creds_or_platform_llm_grants(self) -> None:
|
|
agent_source = Path(__file__).resolve().parents[1].joinpath("agent.py").read_text()
|
|
|
|
self.assertIn(
|
|
"llm_provisioning = LLMProvisioning.PLATFORM_OR_CALLER_PROVIDED",
|
|
agent_source,
|
|
)
|
|
self.assertIn("caller_pays_llm=False", agent_source)
|
|
self.assertIn("platform-scoped LiteLLM grant", agent_source)
|
|
self.assertIn("caller-selected LLM credentials", agent_source)
|
|
|
|
def test_builder_defaults_raise_timeout_budget(self) -> None:
|
|
config_source = Path(__file__).resolve().parents[1].joinpath(
|
|
"agent_builder/config.py"
|
|
).read_text()
|
|
|
|
self.assertIn('os.environ.get("A2A_SANDBOX_TIMEOUT_S")', config_source)
|
|
self.assertIn('os.environ.get("SANDBOX_TIMEOUT_S", "1200")', config_source)
|
|
self.assertIn('deploy_wait_timeout_s=int(os.environ.get("DEPLOY_WAIT_TIMEOUT_S", "900"))', config_source)
|
|
|
|
def test_builder_requires_frontend_capable_a2a_pack(self) -> None:
|
|
root = Path(__file__).resolve().parents[1]
|
|
requirements = root.joinpath("requirements.txt").read_text()
|
|
tools_source = root.joinpath("agent_builder/tools.py").read_text()
|
|
|
|
self.assertIn(f"a2a-pack>={A2A_PACK_MIN_VERSION}", requirements)
|
|
self.assertIn("a2a-pack>={A2A_PACK_MIN_VERSION}", tools_source)
|
|
|
|
def test_prompt_requires_resource_mirror_for_heavy_agents(self) -> None:
|
|
self.assertIn("declare resources in BOTH places", SYSTEM_PROMPT)
|
|
self.assertIn("resources = Resources", SYSTEM_PROMPT)
|
|
self.assertIn("runtime:", SYSTEM_PROMPT)
|
|
self.assertIn("max_runtime_seconds", SYSTEM_PROMPT)
|
|
|
|
def test_prompt_requires_live_schema_check_and_manim_contract(self) -> None:
|
|
self.assertIn("live_skills[].input_schema", SYSTEM_PROMPT)
|
|
self.assertIn("class GeneratedDeck(Slide)", SYSTEM_PROMPT)
|
|
self.assertIn("manim-slides render --CE -ql", SYSTEM_PROMPT)
|
|
self.assertIn("render: bool = False", SYSTEM_PROMPT)
|
|
self.assertIn('config={"recursion_limit": 500}', SYSTEM_PROMPT)
|
|
|
|
def test_prompt_requires_workspace_mounted_execution_for_outputs(self) -> None:
|
|
self.assertIn("ctx.workspace_shell", SYSTEM_PROMPT)
|
|
self.assertIn("ctx.workspace_python", SYSTEM_PROMPT)
|
|
self.assertIn("outputs/rootfs-captures/...", SYSTEM_PROMPT)
|
|
self.assertIn("not workspace-mounted or rootfs-captured", SYSTEM_PROMPT)
|
|
|
|
def test_prompt_knows_packed_frontend_contract(self) -> None:
|
|
self.assertIn('init_agent_template(..., frontend="react")', SYSTEM_PROMPT)
|
|
self.assertIn("frontend:", SYSTEM_PROMPT)
|
|
self.assertIn("mount: /app", SYSTEM_PROMPT)
|
|
self.assertIn("/app/config.json", SYSTEM_PROMPT)
|
|
self.assertIn("/app/a2a-client.js", SYSTEM_PROMPT)
|
|
self.assertIn("https://docs.a2acloud.io/concepts/packed-frontends", SYSTEM_PROMPT)
|
|
self.assertIn("frontend/src/App.jsx", SYSTEM_PROMPT)
|
|
|
|
def test_prompt_and_backend_load_packaged_builder_skills(self) -> None:
|
|
expected = {
|
|
"deepagent-agent-design",
|
|
"a2apack-agent-authoring",
|
|
"deepagents-implementation-patterns",
|
|
"workspace-artifact-safety",
|
|
"agent-quality-review",
|
|
}
|
|
for skill_name in expected:
|
|
self.assertIn(skill_name, SYSTEM_PROMPT)
|
|
self.assertIn("deepagent-agent-design", SYSTEM_PROMPT)
|
|
self.assertIn("write_agent_skill", SYSTEM_PROMPT)
|
|
self.assertIn("workspace_access = WorkspaceAccess.dynamic", SYSTEM_PROMPT)
|
|
self.assertIn("ensure_read/ensure_write", SYSTEM_PROMPT)
|
|
|
|
files = _builder_skill_files()
|
|
self.assertTrue(
|
|
{path.split("/", 1)[0] for path in files}.issuperset(expected)
|
|
)
|
|
self.assertIn("skills=skill_sources or None", files["deepagent-agent-design/SKILL.md"])
|
|
self.assertIn("from a2a_pack import", files["a2apack-agent-authoring/SKILL.md"])
|
|
self.assertIn("ctx.workspace_shell", files["a2apack-agent-authoring/SKILL.md"])
|
|
self.assertIn("create_deep_agent", files["deepagents-implementation-patterns/SKILL.md"])
|
|
self.assertIn('config={"recursion_limit": 500}', files["deepagents-implementation-patterns/SKILL.md"])
|
|
self.assertIn("ctx.write_artifact", files["workspace-artifact-safety/SKILL.md"])
|
|
self.assertIn("ctx.workspace_shell", files["workspace-artifact-safety/SKILL.md"])
|
|
self.assertIn("write_prefixes", files["workspace-artifact-safety/SKILL.md"])
|
|
self.assertIn("outputs/rootfs-captures/...", files["workspace-artifact-safety/SKILL.md"])
|
|
self.assertIn("live_skills[].input_schema", files["agent-quality-review/SKILL.md"])
|
|
self.assertIn("ctx.workspace_python", files["agent-quality-review/SKILL.md"])
|
|
|
|
backend = _with_builder_skills(StateBackend())
|
|
listing = backend.ls(BUILDER_SKILL_SOURCE)
|
|
paths = [entry["path"] for entry in (listing.entries or [])]
|
|
for skill_name in expected:
|
|
self.assertIn(f"{BUILDER_SKILL_SOURCE}{skill_name}/", paths)
|