Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions agents/form/src/form/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,12 @@


class FormData(BaseModel):
location: str | None
date_from: str | None
date_to: str | None
notes: list[FileInfo] | None
flexible: bool | None
interests: list[str] | None
location: str
date_from: str | None = None
date_to: str | None = None
notes: list[FileInfo] | None = None
flexible: bool | None = None
interests: list[str] | None = None


@server.agent(
Expand Down Expand Up @@ -108,9 +108,10 @@ async def agent(
):
"""Example demonstrating a single-turn agent using a form to collect user input."""

form_data = form.parse_initial_form(model=FormData)

yield f"Hello {form_data.location}"
if form_data := form.parse_initial_form(model=FormData):
yield f"Hello {form_data.location}"
else:
yield "No form data received."


def serve():
Expand Down
4 changes: 4 additions & 0 deletions agentstack.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
"name": "agent-chat",
"path": "agents/chat"
},
{
"name": "agent-form",
"path": "agents/form"
},
{
"name": "agent-rag",
"path": "agents/rag"
Expand Down
2 changes: 0 additions & 2 deletions apps/agentstack-cli/src/agentstack_cli/commands/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -880,8 +880,6 @@ async def run_agent(
] = None,
) -> None:
"""Run an agent."""
if search_path is not None and input is None and sys.stdin.isatty():
input = sys.stdin.read()
async with configuration.use_platform_client():
providers = await Provider.list()
await ensure_llm_provider()
Expand Down
15 changes: 13 additions & 2 deletions apps/agentstack-cli/src/agentstack_cli/commands/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ async def client_side_build(
context_hash = hashlib.sha256((context + (dockerfile or "")).encode()).hexdigest()[:6]
context_shorter = re.sub(r"https?://", "", context).replace(r".git", "")
context_shorter = re.sub(r"[^a-zA-Z0-9_-]+", "-", context_shorter)[:32].lstrip("-") or "provider"
tag = (tag or f"agentstack.local/{context_shorter}-{context_hash}:latest").lower()
tag = (tag or f"agentstack-registry-svc.default:5001/{context_shorter}-{context_hash}:latest").lower()
await run_command(
command=[
*(
Expand All @@ -135,11 +135,22 @@ async def client_side_build(
if import_image:
from agentstack_cli.commands.platform import get_driver

if "agentstack-registry-svc.default" not in tag:
source_tag = tag
tag = re.sub("^[^/]*/", "agentstack-registry-svc.default:5001/", tag)
await run_command(["docker", "tag", source_tag, tag], "Tagging image")

driver = get_driver(vm_name=vm_name)

if (await driver.status()) != "running":
console.error("Agent Stack platform is not running.")
sys.exit(1)
await driver.import_image(tag)

await driver.import_image_to_internal_registry(tag)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The check to ensure the Agent Stack platform is running has been removed. This could lead to cryptic error messages if the platform is not running when driver.import_image_to_internal_registry(tag) is called. It's better to explicitly check the platform status and provide a clear error message to the user, as was done previously. Please add a check like the following before this line:

if (await driver.status()) != "running":
    console.error("Agent Stack platform is not running. Please start it with 'agentstack platform start'.")
    raise typer.Exit(1)

console.success(
"Agent was imported to the agent stack internal registry.\n"
+ f"You can add it using [blue]agentstack add {tag}[/blue]"
)

return tag, agent_card

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class BaseDriver(abc.ABC):

def __init__(self, vm_name: str = "agentstack"):
self.vm_name = vm_name
self.loaded_images: set[str] = set()

@abc.abstractmethod
async def run_in_vm(
Expand All @@ -47,6 +48,9 @@ async def delete(self) -> None: ...
@abc.abstractmethod
async def import_image(self, tag: str) -> None: ...

@abc.abstractmethod
async def import_image_to_internal_registry(self, tag: str) -> None: ...

@abc.abstractmethod
async def exec(self, command: list[str]) -> None: ...

Expand Down Expand Up @@ -138,20 +142,17 @@ async def deploy(
).stdout.decode()
for image in import_images or []:
await self.import_image(image)
self.loaded_images.add(image)
for image in {typing.cast(str, yaml.safe_load(line)) for line in images_str.splitlines()} - set(
import_images or []
):
async for attempt in AsyncRetrying(stop=stop_after_attempt(5)):
with attempt:
attempt_num = attempt.retry_state.attempt_number
image_id = image if "." in image.split("/")[0] else f"docker.io/{image}"
self.loaded_images.add(image_id)
await self.run_in_vm(
[
"k3s",
"ctr",
"image",
"pull",
image if "." in image.split("/")[0] else f"docker.io/{image}",
],
["k3s", "ctr", "image", "pull", image_id],
f"Pulling image {image}" + (f" (attempt {attempt_num})" if attempt_num > 1 else ""),
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,72 @@ async def import_image(self, tag: str):
finally:
await image_path.unlink(missing_ok=True)

@typing.override
async def import_image_to_internal_registry(self, tag: str) -> None:
# 1. Check if registry is running
try:
await self.run_in_vm(
["k3s", "kubectl", "get", "svc", "agentstack-registry-svc"],
"Checking internal registry availability",
)
except Exception as e:
console.warning(f"Internal registry service not found. Push might fail: {e}")

# 2. Export image from Docker to shared temp dir
image_dir = anyio.Path("/tmp/agentstack")
await image_dir.mkdir(exist_ok=True, parents=True)
image_file = f"{uuid.uuid4()}.tar"
image_path = image_dir / image_file

try:
await run_command(
["docker", "image", "save", "-o", str(image_path), tag],
f"Exporting image {tag} from Docker",
)

# 3 & 4. Run Crane Job
crane_image = "ghcr.io/i-am-bee/alpine/crane:0.20.6"
for image in self.loaded_images:
if "alpine/crane" in image:
crane_image = image
break

job_name = f"push-{uuid.uuid4().hex[:6]}"
job_def = {
"apiVersion": "batch/v1",
"kind": "Job",
"metadata": {"name": job_name, "namespace": "default"},
"spec": {
"backoffLimit": 0,
"ttlSecondsAfterFinished": 60,
"template": {
"spec": {
"restartPolicy": "Never",
"containers": [
{
"name": "crane",
"image": crane_image,
"command": ["crane", "push", f"/workspace/{image_file}", tag, "--insecure"],
"volumeMounts": [{"name": "workspace", "mountPath": "/workspace"}],
}
],
"volumes": [{"name": "workspace", "hostPath": {"path": "/tmp/agentstack"}}],
}
},
},
}

await self.run_in_vm(
["k3s", "kubectl", "apply", "-f", "-"], "Starting push job", input=yaml.dump(job_def).encode()
)
await self.run_in_vm(
["k3s", "kubectl", "wait", "--for=condition=complete", f"job/{job_name}", "--timeout=300s"],
"Waiting for push to complete",
)
await self.run_in_vm(["k3s", "kubectl", "delete", "job", job_name], "Cleaning up push job")
finally:
await image_path.unlink(missing_ok=True)

@typing.override
async def exec(self, command: list[str]):
await anyio.run_process(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,10 @@ async def delete(self):
async def import_image(self, tag: str) -> None:
raise NotImplementedError("Importing images is not supported on this platform.")

@typing.override
async def import_image_to_internal_registry(self, tag: str) -> None:
raise NotImplementedError("Importing images to internal registry is not supported on this platform.")

@typing.override
async def exec(self, command: list[str]):
await anyio.run_process(
Expand Down