fix: require current a2a init scaffold
All checks were successful
build / build (push) Successful in 26s

This commit is contained in:
robert
2026-05-17 08:08:04 -03:00
parent 54a853b9df
commit 23cdc1e924
5 changed files with 60 additions and 28 deletions

View File

@@ -4,6 +4,7 @@ WORKDIR /app
COPY requirements.txt . COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt RUN pip install --no-cache-dir -r requirements.txt
RUN python -c "import importlib.resources as r, a2a_pack; t = r.files('a2a_pack.cli.templates').joinpath('agent.py.tmpl').read_text(); missing = [m for m in ('LLMProvisioning.CALLER_PROVIDED', 'ctx.llm', 'create_deep_agent', 'wrap_model_call') if m not in t]; assert not missing, f'a2a-pack {a2a_pack.__version__} has stale init template; missing {missing}'"
COPY . . COPY . .

View File

@@ -30,37 +30,19 @@ Given a user description, you write a complete agent project under the
user's workspace at ``agents/<name>/`` and then deploy it through the user's workspace at ``agents/<name>/`` and then deploy it through the
control plane. control plane.
What an a2a-pack agent looks like: The default starter is the current a2a-pack DeepAgents scaffold. It declares
``LLMProvisioning.CALLER_PROVIDED``, reads caller LLM credentials from
``ctx.llm``, builds a ``ChatOpenAI`` model from those credentials, and wires a
small tool-calling DeepAgent with ``create_deep_agent`` plus
``wrap_model_call`` middleware. Do not recreate this from memory: call
``init_agent_template`` first and modify the generated files.
```python The generated ``a2a.yaml`` looks like:
from pydantic import BaseModel
from a2a_pack import A2AAgent, NoAuth, RunContext, skill
class <Name>Config(BaseModel):
pass
class <Name>(A2AAgent[<Name>Config, NoAuth]):
name = "<slug>"
description = "..."
version = "0.1.0"
config_model = <Name>Config
auth_model = NoAuth
@skill(description="...", tags=["..."])
async def <skill>(self, ctx: RunContext[NoAuth], ...) -> dict:
await ctx.emit_progress("...")
return {"ok": True, "...": "..."}
```
You ALSO need an ``a2a.yaml`` like:
```yaml ```yaml
name: <slug> name: <slug>
version: 0.1.0 version: 0.1.0
entrypoint: agent:<Name> entrypoint: agent:<Name>
description: <one line>
expose: expose:
public: true public: true
``` ```

View File

@@ -128,7 +128,7 @@ def build_tools(ctx: ToolContext) -> list[Any]:
try: try:
files = _render_a2a_init_template(name, description=description) files = _render_a2a_init_template(name, description=description)
prefix = _agent_prefix(name) prefix = _agent_prefix(name)
except (OSError, ValueError) as exc: except (OSError, RuntimeError, ValueError) as exc:
return json.dumps({"error": str(exc)}) return json.dumps({"error": str(exc)})
written: list[dict[str, Any]] = [] written: list[dict[str, Any]] = []
for path, content in files.items(): for path, content in files.items():
@@ -519,7 +519,7 @@ def _render_a2a_init_template(
) -> dict[str, str]: ) -> dict[str, str]:
_validate_name(name) _validate_name(name)
class_name = _class_name(name) class_name = _class_name(name)
return { files = {
"agent.py": _render_sdk_template( "agent.py": _render_sdk_template(
"agent.py.tmpl", "agent.py.tmpl",
name=name, name=name,
@@ -533,6 +533,44 @@ def _render_a2a_init_template(
), ),
"requirements.txt": _render_sdk_template("requirements.txt.tmpl"), "requirements.txt": _render_sdk_template("requirements.txt.tmpl"),
} }
_assert_current_a2a_init_template(files)
return files
def _assert_current_a2a_init_template(files: dict[str, str]) -> None:
required_markers = {
"agent.py": (
"LLMProvisioning.CALLER_PROVIDED",
"ctx.llm",
"create_deep_agent",
"wrap_model_call",
"ChatOpenAI",
),
"requirements.txt": (
"deepagents>=0.5.0",
"langchain-openai",
),
}
missing: list[str] = []
for path, markers in required_markers.items():
content = files.get(path, "")
for marker in markers:
if marker not in content:
missing.append(f"{path}:{marker}")
if not missing:
return
try:
import a2a_pack
version = getattr(a2a_pack, "__version__", "unknown")
except Exception: # noqa: BLE001
version = "unknown"
raise RuntimeError(
"installed a2a-pack init template is stale; expected the 0.1.3+ "
"DeepAgents caller-LLM scaffold. "
f"a2a_pack={version}; missing markers: {', '.join(missing)}"
)
def _render_sdk_template(template: str, /, **vars: str) -> str: def _render_sdk_template(template: str, /, **vars: str) -> str:

View File

@@ -1,3 +1,4 @@
a2a-pack>=0.1.3
httpx>=0.27 httpx>=0.27
boto3>=1.34 boto3>=1.34
deepagents>=0.5.0 deepagents>=0.5.0

View File

@@ -2,7 +2,10 @@ from __future__ import annotations
import unittest import unittest
from agent_builder.tools import _render_a2a_init_template from agent_builder.tools import (
_assert_current_a2a_init_template,
_render_a2a_init_template,
)
class TemplateInitTests(unittest.TestCase): class TemplateInitTests(unittest.TestCase):
@@ -25,3 +28,10 @@ class TemplateInitTests(unittest.TestCase):
def test_render_a2a_init_template_rejects_invalid_names(self) -> None: def test_render_a2a_init_template_rejects_invalid_names(self) -> None:
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
_render_a2a_init_template("Bad Name") _render_a2a_init_template("Bad Name")
def test_stale_sdk_template_is_rejected(self) -> None:
with self.assertRaisesRegex(RuntimeError, "stale"):
_assert_current_a2a_init_template({
"agent.py": "from a2a_pack import A2AAgent",
"requirements.txt": "httpx>=0.27",
})