fix: align agent-builder with longer pack timeouts
All checks were successful
build / build (push) Successful in 27s
All checks were successful
build / build (push) Successful in 27s
This commit is contained in:
14
agent.py
14
agent.py
@@ -17,8 +17,10 @@ because it needs three things forwarded from the caller:
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
from contextlib import suppress
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
@@ -65,6 +67,7 @@ class AgentBuilder(A2AAgent[BuilderConfig, NoAuth]):
|
|||||||
"should do. Returns the live URL when ready."
|
"should do. Returns the live URL when ready."
|
||||||
),
|
),
|
||||||
tags=["builder", "scaffold", "deepagents", "meta"],
|
tags=["builder", "scaffold", "deepagents", "meta"],
|
||||||
|
stream=True,
|
||||||
)
|
)
|
||||||
async def build(
|
async def build(
|
||||||
self,
|
self,
|
||||||
@@ -127,6 +130,13 @@ class AgentBuilder(A2AAgent[BuilderConfig, NoAuth]):
|
|||||||
final_state: dict[str, Any] = {}
|
final_state: dict[str, Any] = {}
|
||||||
deployed_url: str | None = None
|
deployed_url: str | None = None
|
||||||
last_reply: str = ""
|
last_reply: str = ""
|
||||||
|
|
||||||
|
async def _heartbeat() -> None:
|
||||||
|
while True:
|
||||||
|
await asyncio.sleep(20)
|
||||||
|
await ctx.emit_progress(f"agent-builder still working on {name!r}")
|
||||||
|
|
||||||
|
heartbeat_task = asyncio.create_task(_heartbeat())
|
||||||
try:
|
try:
|
||||||
async for event in graph.astream_events(
|
async for event in graph.astream_events(
|
||||||
{"messages": [{"role": "user", "content": user_msg}]},
|
{"messages": [{"role": "user", "content": user_msg}]},
|
||||||
@@ -156,6 +166,10 @@ class AgentBuilder(A2AAgent[BuilderConfig, NoAuth]):
|
|||||||
final_state = out
|
final_state = out
|
||||||
except Exception as exc: # noqa: BLE001
|
except Exception as exc: # noqa: BLE001
|
||||||
return {"error": f"build graph failed: {type(exc).__name__}: {exc}"}
|
return {"error": f"build graph failed: {type(exc).__name__}: {exc}"}
|
||||||
|
finally:
|
||||||
|
heartbeat_task.cancel()
|
||||||
|
with suppress(asyncio.CancelledError):
|
||||||
|
await heartbeat_task
|
||||||
|
|
||||||
for msg in final_state.get("messages") or []:
|
for msg in final_state.get("messages") or []:
|
||||||
content = msg.get("content") if isinstance(msg, dict) else getattr(msg, "content", None)
|
content = msg.get("content") if isinstance(msg, dict) else getattr(msg, "content", None)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from dataclasses import dataclass
|
|||||||
class Settings:
|
class Settings:
|
||||||
sandbox_url: str
|
sandbox_url: str
|
||||||
sandbox_timeout_s: int
|
sandbox_timeout_s: int
|
||||||
|
deploy_wait_timeout_s: int
|
||||||
litellm_url: str
|
litellm_url: str
|
||||||
litellm_key: str
|
litellm_key: str
|
||||||
litellm_model: str
|
litellm_model: str
|
||||||
@@ -24,7 +25,8 @@ def load_settings() -> Settings:
|
|||||||
sandbox_url=os.environ.get(
|
sandbox_url=os.environ.get(
|
||||||
"SANDBOX_URL", "http://sandbox.sandbox.svc.cluster.local:8000"
|
"SANDBOX_URL", "http://sandbox.sandbox.svc.cluster.local:8000"
|
||||||
),
|
),
|
||||||
sandbox_timeout_s=int(os.environ.get("SANDBOX_TIMEOUT_S", "240")),
|
sandbox_timeout_s=int(os.environ.get("SANDBOX_TIMEOUT_S", "1200")),
|
||||||
|
deploy_wait_timeout_s=int(os.environ.get("DEPLOY_WAIT_TIMEOUT_S", "900")),
|
||||||
litellm_url=os.environ.get(
|
litellm_url=os.environ.get(
|
||||||
"A2A_LITELLM_URL", "http://litellm.llm.svc.cluster.local:4000"
|
"A2A_LITELLM_URL", "http://litellm.llm.svc.cluster.local:4000"
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class ToolContext:
|
|||||||
async def _wait_for_live_card(
|
async def _wait_for_live_card(
|
||||||
url: str | None,
|
url: str | None,
|
||||||
expected_version: str | None,
|
expected_version: str | None,
|
||||||
timeout_s: float = 180.0,
|
timeout_s: float = 900.0,
|
||||||
) -> tuple[bool, dict[str, Any]]:
|
) -> tuple[bool, dict[str, Any]]:
|
||||||
"""Poll ``{url}/.well-known/agent-card`` until ``version`` matches
|
"""Poll ``{url}/.well-known/agent-card`` until ``version`` matches
|
||||||
``expected_version`` or ``timeout_s`` elapses. Returns
|
``expected_version`` or ``timeout_s`` elapses. Returns
|
||||||
@@ -435,7 +435,11 @@ def build_tools(ctx: ToolContext) -> list[Any]:
|
|||||||
# so the LLM can verify the input_schema actually matches the
|
# so the LLM can verify the input_schema actually matches the
|
||||||
# code it wrote — catches the schema/code skew that causes
|
# code it wrote — catches the schema/code skew that causes
|
||||||
# downstream 400s on call_agent.
|
# downstream 400s on call_agent.
|
||||||
live, live_card = await _wait_for_live_card(url_, version_)
|
live, live_card = await _wait_for_live_card(
|
||||||
|
url_,
|
||||||
|
version_,
|
||||||
|
timeout_s=settings.deploy_wait_timeout_s,
|
||||||
|
)
|
||||||
out: dict[str, Any] = {
|
out: dict[str, Any] = {
|
||||||
"ok": live,
|
"ok": live,
|
||||||
"name": name_,
|
"name": name_,
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ spec:
|
|||||||
value: registry.a2acloud.io/agents/agent-builder:f7da050814bf8856fb2df0949d8140b34d379864
|
value: registry.a2acloud.io/agents/agent-builder:f7da050814bf8856fb2df0949d8140b34d379864
|
||||||
- name: SANDBOX_URL
|
- name: SANDBOX_URL
|
||||||
value: http://sandbox.sandbox.svc.cluster.local:8000
|
value: http://sandbox.sandbox.svc.cluster.local:8000
|
||||||
|
- name: SANDBOX_TIMEOUT_S
|
||||||
|
value: "1200"
|
||||||
|
- name: DEPLOY_WAIT_TIMEOUT_S
|
||||||
|
value: "900"
|
||||||
- name: A2A_LITELLM_URL
|
- name: A2A_LITELLM_URL
|
||||||
value: http://litellm.llm.svc.cluster.local:4000
|
value: http://litellm.llm.svc.cluster.local:4000
|
||||||
- name: A2A_LITELLM_KEY
|
- name: A2A_LITELLM_KEY
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
a2a-pack>=0.1.12
|
a2a-pack>=0.1.14
|
||||||
httpx>=0.27
|
httpx>=0.27
|
||||||
boto3>=1.34
|
boto3>=1.34
|
||||||
deepagents>=0.5.0
|
deepagents>=0.5.0
|
||||||
|
|||||||
@@ -21,6 +21,15 @@ class BuilderPromptTests(unittest.TestCase):
|
|||||||
"config={\"recursion_limit\": DEEPAGENTS_RECURSION_LIMIT}",
|
"config={\"recursion_limit\": DEEPAGENTS_RECURSION_LIMIT}",
|
||||||
agent_source,
|
agent_source,
|
||||||
)
|
)
|
||||||
|
self.assertIn("stream=True", 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('sandbox_timeout_s=int(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_prompt_requires_resource_mirror_for_heavy_agents(self) -> None:
|
def test_prompt_requires_resource_mirror_for_heavy_agents(self) -> None:
|
||||||
self.assertIn("declare resources in BOTH places", SYSTEM_PROMPT)
|
self.assertIn("declare resources in BOTH places", SYSTEM_PROMPT)
|
||||||
|
|||||||
Reference in New Issue
Block a user