fix: persist builder deepagent files
All checks were successful
build / build (push) Successful in 31s

This commit is contained in:
robert
2026-05-17 18:57:29 -03:00
parent 34cbaa702d
commit de2887ff6f
6 changed files with 61 additions and 5 deletions

View File

@@ -4,7 +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}'" 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', 'ctx.workspace_backend()', '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

@@ -108,6 +108,7 @@ class AgentBuilder(A2AAgent[BuilderConfig, NoAuth]):
graph = build_agent_builder(BuilderContext( graph = build_agent_builder(BuilderContext(
bucket=bucket, cp_jwt=cp_jwt, bucket=bucket, cp_jwt=cp_jwt,
project_prefix=f"agents/{name}",
llm_base_url=creds.base_url, llm_base_url=creds.base_url,
llm_api_key=creds.api_key, llm_api_key=creds.api_key,
llm_model=creds.model, llm_model=creds.model,

View File

@@ -16,6 +16,7 @@ class BuilderContext:
bucket: str bucket: str
cp_jwt: str | None = None cp_jwt: str | None = None
settings: Settings | None = None settings: Settings | None = None
project_prefix: str | None = None
# Optional caller-provided LLM creds (when the outer platform Card # Optional caller-provided LLM creds (when the outer platform Card
# declares llm_provisioning=caller_provided). Falls back to settings. # declares llm_provisioning=caller_provided). Falls back to settings.
llm_base_url: str | None = None llm_base_url: str | None = None
@@ -128,6 +129,58 @@ def build_agent_builder(ctx: BuilderContext) -> Any:
api_key=ctx.llm_api_key or settings.litellm_key, api_key=ctx.llm_api_key or settings.litellm_key,
temperature=0.0, temperature=0.0,
) )
return create_deep_agent( kwargs: dict[str, Any] = {
model=model, tools=tools, system_prompt=SYSTEM_PROMPT, "model": model,
"tools": tools,
"system_prompt": SYSTEM_PROMPT,
}
backend = _build_project_backend(ctx, settings=settings)
if backend is not None:
kwargs["backend"] = backend
return create_deep_agent(**kwargs)
def _build_project_backend(ctx: BuilderContext, *, settings: Settings) -> Any | None:
"""Persist DeepAgents built-in file tools into this project directory.
The explicit builder tools remain the preferred path, but this prevents
a model-selected DeepAgents ``write_file`` from disappearing into
LangGraph state. Scope it to the project prefix, not the whole bucket.
"""
if not ctx.project_prefix:
return None
prefix = ctx.project_prefix.strip("/")
if not prefix:
return None
try:
from a2a_pack import Grant, WorkspaceAccess, WorkspaceMode
from a2a_pack.deepagents import WorkspaceBackend
from a2a_pack.workspace import MinIOWorkspaceClient
except Exception: # noqa: BLE001
return None
workspace = MinIOWorkspaceClient(
bucket=ctx.bucket,
endpoint_url=settings.minio_endpoint,
access_key_id=settings.minio_access_key,
secret_access_key=settings.minio_secret_key,
access=WorkspaceAccess.dynamic(
max_files=2000,
allowed_modes=(WorkspaceMode.READ_ONLY, WorkspaceMode.READ_WRITE_OVERLAY),
require_reason=False,
),
issuer="agent-builder",
) )
workspace.install_grant(
Grant(
grant_id=f"agent-builder-{prefix.replace('/', '-')}",
issuer="agent-builder",
audience="agent-builder",
bucket=ctx.bucket,
mode=WorkspaceMode.READ_WRITE_OVERLAY,
allow_patterns=(f"{prefix}/**", "outputs/**"),
deny_patterns=(),
outputs_prefix=None,
)
)
return WorkspaceBackend(workspace)

View File

@@ -599,6 +599,7 @@ def _assert_current_deepagents_scaffold(
"agent.py": ( "agent.py": (
"LLMProvisioning.CALLER_PROVIDED", "LLMProvisioning.CALLER_PROVIDED",
"ctx.llm", "ctx.llm",
"ctx.workspace_backend()",
"create_deep_agent", "create_deep_agent",
"wrap_model_call", "wrap_model_call",
"ChatOpenAI", "ChatOpenAI",
@@ -624,7 +625,7 @@ def _assert_current_deepagents_scaffold(
except Exception: # noqa: BLE001 except Exception: # noqa: BLE001
version = "unknown" version = "unknown"
raise RuntimeError( raise RuntimeError(
f"{source} is stale; expected the 0.1.3+ " f"{source} is stale; expected the 0.1.4+ "
"DeepAgents caller-LLM scaffold. " "DeepAgents caller-LLM scaffold. "
f"a2a_pack={version}; missing markers: {', '.join(missing)}" f"a2a_pack={version}; missing markers: {', '.join(missing)}"
) )

View File

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

View File

@@ -23,6 +23,7 @@ class TemplateInitTests(unittest.TestCase):
self.assertIn('description = "Research helper"', files["agent.py"]) self.assertIn('description = "Research helper"', files["agent.py"])
self.assertIn("LLMProvisioning.CALLER_PROVIDED", files["agent.py"]) self.assertIn("LLMProvisioning.CALLER_PROVIDED", files["agent.py"])
self.assertIn("ctx.llm", files["agent.py"]) self.assertIn("ctx.llm", files["agent.py"])
self.assertIn("ctx.workspace_backend()", files["agent.py"])
self.assertIn("create_deep_agent", files["agent.py"]) self.assertIn("create_deep_agent", files["agent.py"])
self.assertIn("name: research-agent", files["a2a.yaml"]) self.assertIn("name: research-agent", files["a2a.yaml"])
self.assertIn("entrypoint: agent:ResearchAgent", files["a2a.yaml"]) self.assertIn("entrypoint: agent:ResearchAgent", files["a2a.yaml"])