diff --git a/agent.py b/agent.py index b975416..1806031 100644 --- a/agent.py +++ b/agent.py @@ -190,7 +190,7 @@ class AgentBuilder(A2AAgent[BuilderConfig, NoAuth]): if isinstance(out, dict): final_state = out except Exception as exc: # noqa: BLE001 - return {"error": f"build graph failed: {type(exc).__name__}: {exc}"} + return {"error": _build_failure_error(exc)} finally: heartbeat_task.cancel() with suppress(asyncio.CancelledError): @@ -278,4 +278,20 @@ def _find_url(value: Any) -> str | None: m = _URL_RE.search(value) return m.group(0) if m else None return None + + +def _build_failure_error(exc: BaseException) -> str: + message = str(exc) + lowered = message.lower() + if ( + "budget has been exceeded" in lowered + or "budget exceeded" in lowered + or "ratelimiterror" in type(exc).__name__.lower() + or "rate limit" in lowered + ): + return ( + "LLM budget exceeded before deployment; retry with a larger budget " + "or a smaller build scope" + ) + return f"build graph failed: {type(exc).__name__}: {exc}" # rebuild against a2a-pack 0.1.47 for manifest-backed meta-agent defaults diff --git a/agent_builder/tools.py b/agent_builder/tools.py index 0908942..3980f4f 100644 --- a/agent_builder/tools.py +++ b/agent_builder/tools.py @@ -29,7 +29,7 @@ if TYPE_CHECKING: from .config import Settings -A2A_PACK_MIN_VERSION = "0.1.47" +A2A_PACK_MIN_VERSION = "0.1.51" @dataclass(frozen=True) diff --git a/requirements.txt b/requirements.txt index dafd889..79e8d2b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -a2a-pack>=0.1.47 +a2a-pack>=0.1.51 httpx>=0.27 boto3>=1.34 deepagents>=0.5.0 diff --git a/tests/test_builder_prompt.py b/tests/test_builder_prompt.py index c1cf226..893d26b 100644 --- a/tests/test_builder_prompt.py +++ b/tests/test_builder_prompt.py @@ -14,6 +14,7 @@ from agent_builder.builder import ( from agent_builder.config import Settings from agent_builder.tools import A2A_PACK_MIN_VERSION from deepagents.backends import StateBackend +from agent import _build_failure_error class BuilderPromptTests(unittest.TestCase): @@ -132,6 +133,13 @@ class BuilderPromptTests(unittest.TestCase): for skill_name in expected: self.assertIn(f"{BUILDER_SKILL_SOURCE}{skill_name}/", paths) + def test_build_failure_error_classifies_budget_exhaustion(self) -> None: + self.assertEqual( + _build_failure_error(RuntimeError("Budget has been exceeded! Key=abc")), + "LLM budget exceeded before deployment; retry with a larger budget " + "or a smaller build scope", + ) + def test_project_backend_keeps_deepagents_artifacts_inside_project_grant(self) -> None: backend = _build_project_backend( BuilderContext(