diff --git a/.github/workflows/build-and-push-tutorial-agent.yml b/.github/workflows/build-and-push-tutorial-agent.yml index f58555fe3..1c9b58636 100644 --- a/.github/workflows/build-and-push-tutorial-agent.yml +++ b/.github/workflows/build-and-push-tutorial-agent.yml @@ -211,7 +211,7 @@ jobs: fi # Build command - add --push only if we should push - BUILD_ARGS="--manifest ${{ matrix.agent_path }}/manifest.yaml --registry ${REGISTRY} --tag ${VERSION_TAG} --platforms linux/amd64 --repository-name ${REPOSITORY_NAME}" + BUILD_ARGS="--manifest ${{ matrix.agent_path }}/manifest.yaml --registry ${REGISTRY} --tag ${VERSION_TAG} --platforms linux/amd64,linux/arm64 --repository-name ${REPOSITORY_NAME}" if [ "$SHOULD_PUSH" = "true" ]; then agentex agents build $BUILD_ARGS --push diff --git a/examples/tutorials/00_sync/000_hello_acp/Dockerfile b/examples/tutorials/00_sync/000_hello_acp/Dockerfile index 9550d8e5d..b91d13397 100644 --- a/examples/tutorials/00_sync/000_hello_acp/Dockerfile +++ b/examples/tutorials/00_sync/000_hello_acp/Dockerfile @@ -24,19 +24,28 @@ ENV UV_HTTP_TIMEOUT=1000 # Copy pyproject.toml and README.md to install dependencies -COPY 000_hello_acp/pyproject.toml /app/000_hello_acp/pyproject.toml -COPY 000_hello_acp/README.md /app/000_hello_acp/README.md +COPY 00_sync/000_hello_acp/pyproject.toml /app/000_hello_acp/pyproject.toml +COPY 00_sync/000_hello_acp/README.md /app/000_hello_acp/README.md WORKDIR /app/000_hello_acp # Copy the project code -COPY 000_hello_acp/project /app/000_hello_acp/project +COPY 00_sync/000_hello_acp/project /app/000_hello_acp/project -# Install the required Python packages from pyproject.toml -RUN uv pip install --system . +# Copy the test files +COPY 00_sync/000_hello_acp/tests /app/000_hello_acp/tests + +# Copy shared test utilities +COPY test_utils /app/test_utils + +# Install the required Python packages with dev dependencies +RUN uv pip install --system .[dev] # Set environment variables ENV PYTHONPATH=/app +# Set test environment variables +ENV AGENT_NAME=000-hello-acp + # Run the agent using uvicorn CMD ["uvicorn", "project.acp:acp", "--host", "0.0.0.0", "--port", "8000"] diff --git a/examples/tutorials/00_sync/000_hello_acp/manifest.yaml b/examples/tutorials/00_sync/000_hello_acp/manifest.yaml index 8d9de0c8a..37214b06e 100644 --- a/examples/tutorials/00_sync/000_hello_acp/manifest.yaml +++ b/examples/tutorials/00_sync/000_hello_acp/manifest.yaml @@ -15,24 +15,25 @@ build: context: # Root directory for the build context - root: ../ # Keep this as the default root + root: ../../ # Up to tutorials level to include test_utils # Paths to include in the Docker build context # Must include: # - Your agent's directory (your custom agent code) # These paths are collected and sent to the Docker daemon for building include_paths: - - 000_hello_acp + - 00_sync/000_hello_acp + - test_utils # Path to your agent's Dockerfile # This defines how your agent's image is built from the context # Relative to the root directory - dockerfile: 000_hello_acp/Dockerfile + dockerfile: 00_sync/000_hello_acp/Dockerfile # Path to your agent's .dockerignore # Filters unnecessary files from the build context # Helps keep build context small and builds fast - dockerignore: 000_hello_acp/.dockerignore + dockerignore: 00_sync/000_hello_acp/.dockerignore # Local Development Configuration # ----------------------------- diff --git a/examples/tutorials/00_sync/000_hello_acp/pyproject.toml b/examples/tutorials/00_sync/000_hello_acp/pyproject.toml index 0030004d9..71110739a 100644 --- a/examples/tutorials/00_sync/000_hello_acp/pyproject.toml +++ b/examples/tutorials/00_sync/000_hello_acp/pyproject.toml @@ -3,7 +3,7 @@ requires = ["hatchling"] build-backend = "hatchling.build" [project] -name = "s000-hello-acp" +name = "000-hello-acp" version = "0.1.0" description = "An AgentEx agent that just says hello and acknowledges the user's message" readme = "README.md" @@ -11,13 +11,14 @@ requires-python = ">=3.12" dependencies = [ "agentex-sdk", "scale-gp", - "pytest", - "pytest-xdist" ] [project.optional-dependencies] dev = [ "pytest", + "pytest-asyncio", + "pytest-xdist", + "httpx", "black", "isort", "flake8", diff --git a/examples/tutorials/00_sync/010_multiturn/Dockerfile b/examples/tutorials/00_sync/010_multiturn/Dockerfile index b6a56303a..71ccbaf53 100644 --- a/examples/tutorials/00_sync/010_multiturn/Dockerfile +++ b/examples/tutorials/00_sync/010_multiturn/Dockerfile @@ -23,20 +23,29 @@ RUN uv pip install --system --upgrade pip setuptools wheel ENV UV_HTTP_TIMEOUT=1000 # Copy pyproject.toml and README.md to install dependencies -COPY 010_multiturn/pyproject.toml /app/010_multiturn/pyproject.toml -COPY 010_multiturn/README.md /app/010_multiturn/README.md +COPY 00_sync/010_multiturn/pyproject.toml /app/010_multiturn/pyproject.toml +COPY 00_sync/010_multiturn/README.md /app/010_multiturn/README.md WORKDIR /app/010_multiturn # Copy the project code -COPY 010_multiturn/project /app/010_multiturn/project +COPY 00_sync/010_multiturn/project /app/010_multiturn/project -# Install the required Python packages from pyproject.toml -RUN uv pip install --system . +# Copy the test files +COPY 00_sync/010_multiturn/tests /app/010_multiturn/tests + +# Copy shared test utilities +COPY test_utils /app/test_utils + +# Install the required Python packages with dev dependencies +RUN uv pip install --system .[dev] WORKDIR /app/010_multiturn # Set environment variables ENV PYTHONPATH=/app +# Set test environment variables +ENV AGENT_NAME=010-multiturn + # Run the agent using uvicorn CMD ["uvicorn", "project.acp:acp", "--host", "0.0.0.0", "--port", "8000"] diff --git a/examples/tutorials/00_sync/010_multiturn/manifest.yaml b/examples/tutorials/00_sync/010_multiturn/manifest.yaml index 6b10e48b4..c7e094aa6 100644 --- a/examples/tutorials/00_sync/010_multiturn/manifest.yaml +++ b/examples/tutorials/00_sync/010_multiturn/manifest.yaml @@ -15,24 +15,25 @@ build: context: # Root directory for the build context - root: ../ # Keep this as the default root + root: ../../ # Up to tutorials level to include test_utils # Paths to include in the Docker build context # Must include: # - Your agent's directory (your custom agent code) # These paths are collected and sent to the Docker daemon for building include_paths: - - 010_multiturn + - 00_sync/010_multiturn + - test_utils # Path to your agent's Dockerfile # This defines how your agent's image is built from the context # Relative to the root directory - dockerfile: 010_multiturn/Dockerfile + dockerfile: 00_sync/010_multiturn/Dockerfile # Path to your agent's .dockerignore # Filters unnecessary files from the build context # Helps keep build context small and builds fast - dockerignore: 010_multiturn/.dockerignore + dockerignore: 00_sync/010_multiturn/.dockerignore # Local Development Configuration diff --git a/examples/tutorials/00_sync/010_multiturn/pyproject.toml b/examples/tutorials/00_sync/010_multiturn/pyproject.toml index e21513a85..b1d299a18 100644 --- a/examples/tutorials/00_sync/010_multiturn/pyproject.toml +++ b/examples/tutorials/00_sync/010_multiturn/pyproject.toml @@ -3,7 +3,7 @@ requires = ["hatchling"] build-backend = "hatchling.build" [project] -name = "s010-multiturn" +name = "010-multiturn" version = "0.1.0" description = "An AgentEx agent" readme = "README.md" @@ -16,6 +16,8 @@ dependencies = [ [project.optional-dependencies] dev = [ "pytest", + "pytest-asyncio", + "httpx", "black", "isort", "flake8", diff --git a/examples/tutorials/00_sync/020_streaming/Dockerfile b/examples/tutorials/00_sync/020_streaming/Dockerfile index 758655de1..00137d7f9 100644 --- a/examples/tutorials/00_sync/020_streaming/Dockerfile +++ b/examples/tutorials/00_sync/020_streaming/Dockerfile @@ -23,19 +23,28 @@ RUN uv pip install --system --upgrade pip setuptools wheel ENV UV_HTTP_TIMEOUT=1000 # Copy pyproject.toml and README.md to install dependencies -COPY 020_streaming/pyproject.toml /app/020_streaming/pyproject.toml -COPY 020_streaming/README.md /app/020_streaming/README.md +COPY 00_sync/020_streaming/pyproject.toml /app/020_streaming/pyproject.toml +COPY 00_sync/020_streaming/README.md /app/020_streaming/README.md WORKDIR /app/020_streaming # Copy the project code -COPY 020_streaming/project /app/020_streaming/project +COPY 00_sync/020_streaming/project /app/020_streaming/project -# Install the required Python packages from pyproject.toml -RUN uv pip install --system . +# Copy the test files +COPY 00_sync/020_streaming/tests /app/020_streaming/tests + +# Copy shared test utilities +COPY test_utils /app/test_utils + +# Install the required Python packages with dev dependencies +RUN uv pip install --system .[dev] # Set environment variables ENV PYTHONPATH=/app +# Set test environment variables +ENV AGENT_NAME=020-streaming + # Run the agent using uvicorn CMD ["uvicorn", "project.acp:acp", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file diff --git a/examples/tutorials/00_sync/020_streaming/manifest.yaml b/examples/tutorials/00_sync/020_streaming/manifest.yaml index b59afaefd..39a04d0f8 100644 --- a/examples/tutorials/00_sync/020_streaming/manifest.yaml +++ b/examples/tutorials/00_sync/020_streaming/manifest.yaml @@ -15,7 +15,7 @@ build: context: # Root directory for the build context - root: ../ # Keep this as the default root + root: ../../ # Up to tutorials level to include test_utils # Paths to include in the Docker build context # Must include: @@ -23,17 +23,18 @@ build: # These paths are collected and sent to the Docker daemon for building include_paths: - - 020_streaming + - 00_sync/020_streaming + - test_utils # Path to your agent's Dockerfile # This defines how your agent's image is built from the context # Relative to the root directory - dockerfile: 020_streaming/Dockerfile + dockerfile: 00_sync/020_streaming/Dockerfile # Path to your agent's .dockerignore # Filters unnecessary files from the build context # Helps keep build context small and builds fast - dockerignore: 020_streaming/.dockerignore + dockerignore: 00_sync/020_streaming/.dockerignore # Local Development Configuration diff --git a/examples/tutorials/00_sync/020_streaming/pyproject.toml b/examples/tutorials/00_sync/020_streaming/pyproject.toml index 6e95138e9..5ee3dc184 100644 --- a/examples/tutorials/00_sync/020_streaming/pyproject.toml +++ b/examples/tutorials/00_sync/020_streaming/pyproject.toml @@ -3,7 +3,7 @@ requires = ["hatchling"] build-backend = "hatchling.build" [project] -name = "s020-streaming" +name = "020-streaming" version = "0.1.0" description = "An AgentEx agent that does multiturn streaming chat" readme = "README.md" @@ -16,6 +16,8 @@ dependencies = [ [project.optional-dependencies] dev = [ "pytest", + "pytest-asyncio", + "httpx", "black", "isort", "flake8", diff --git a/examples/tutorials/10_async/00_base/000_hello_acp/Dockerfile b/examples/tutorials/10_async/00_base/000_hello_acp/Dockerfile index f9e26e990..8b0d20f88 100644 --- a/examples/tutorials/10_async/00_base/000_hello_acp/Dockerfile +++ b/examples/tutorials/10_async/00_base/000_hello_acp/Dockerfile @@ -23,20 +23,29 @@ RUN uv pip install --system --upgrade pip setuptools wheel ENV UV_HTTP_TIMEOUT=1000 # Copy pyproject.toml and README.md to install dependencies -COPY 000_hello_acp/pyproject.toml /app/000_hello_acp/pyproject.toml -COPY 000_hello_acp/README.md /app/000_hello_acp/README.md +COPY 10_async/00_base/000_hello_acp/pyproject.toml /app/000_hello_acp/pyproject.toml +COPY 10_async/00_base/000_hello_acp/README.md /app/000_hello_acp/README.md WORKDIR /app/000_hello_acp # Copy the project code -COPY 000_hello_acp/project /app/000_hello_acp/project +COPY 10_async/00_base/000_hello_acp/project /app/000_hello_acp/project -# Install the required Python packages from pyproject.toml -RUN uv pip install --system . +# Copy the test files +COPY 10_async/00_base/000_hello_acp/tests /app/000_hello_acp/tests + +# Copy shared test utilities +COPY test_utils /app/test_utils + +# Install the required Python packages with dev dependencies (includes pytest) +RUN uv pip install --system .[dev] pytest-asyncio httpx WORKDIR /app/000_hello_acp # Set environment variables ENV PYTHONPATH=/app +# Set test environment variables +ENV AGENT_NAME=ab000-hello-acp + # Run the agent using uvicorn CMD ["uvicorn", "project.acp:acp", "--host", "0.0.0.0", "--port", "8000"] diff --git a/examples/tutorials/10_async/00_base/000_hello_acp/manifest.yaml b/examples/tutorials/10_async/00_base/000_hello_acp/manifest.yaml index 47ee7c250..ba0c68369 100644 --- a/examples/tutorials/10_async/00_base/000_hello_acp/manifest.yaml +++ b/examples/tutorials/10_async/00_base/000_hello_acp/manifest.yaml @@ -15,24 +15,25 @@ build: context: # Root directory for the build context - root: ../ # Keep this as the default root + root: ../../../ # Up to tutorials level to include test_utils # Paths to include in the Docker build context # Must include: # - Your agent's directory (your custom agent code) # These paths are collected and sent to the Docker daemon for building include_paths: - - 000_hello_acp + - 10_async/00_base/000_hello_acp + - test_utils # Path to your agent's Dockerfile # This defines how your agent's image is built from the context # Relative to the root directory - dockerfile: 000_hello_acp/Dockerfile + dockerfile: 10_async/00_base/000_hello_acp/Dockerfile # Path to your agent's .dockerignore # Filters unnecessary files from the build context # Helps keep build context small and builds fast - dockerignore: 000_hello_acp/.dockerignore + dockerignore: 10_async/00_base/000_hello_acp/.dockerignore # Local Development Configuration diff --git a/examples/tutorials/10_async/00_base/000_hello_acp/tests/test_agent.py b/examples/tutorials/10_async/00_base/000_hello_acp/tests/test_agent.py index 08cac7a7d..ba3444109 100644 --- a/examples/tutorials/10_async/00_base/000_hello_acp/tests/test_agent.py +++ b/examples/tutorials/10_async/00_base/000_hello_acp/tests/test_agent.py @@ -108,25 +108,41 @@ async def test_send_event_and_stream(self, client: AsyncAgentex, agent_id: str): """Test sending an event and streaming the response.""" task_response = await client.agents.create_task(agent_id, params=ParamsCreateTaskRequest(name=uuid.uuid1().hex)) task = task_response.result + assert task is not None + task_creation_found = False + # Poll for the initial task creation message + async for message in poll_messages( + client=client, + task_id=task.id, + timeout=30, + sleep_interval=1.0, + ): + assert isinstance(message, TaskMessage) + if message.content and message.content.type == "text" and message.content.author == "agent": + assert "Hello! I've received your task" in message.content.content + task_creation_found = True + break + + assert task_creation_found, "Task creation message not found in poll" user_message = "Hello, this is a test message!" + stream_timeout = 10 # Collect events from stream all_events = [] # Flags to track what we've received - task_creation_found = False user_echo_found = False agent_response_found = False - async def collect_stream_events(): - nonlocal task_creation_found, user_echo_found, agent_response_found + async def collect_stream_events() -> None: + nonlocal user_echo_found, agent_response_found async for event in stream_agent_response( client=client, task_id=task.id, - timeout=30, + timeout=stream_timeout, ): all_events.append(event) # Check events as they arrive @@ -136,11 +152,8 @@ async def collect_stream_events(): if content.get("content") is None: continue # Skip empty content if content.get("type") == "text" and content.get("author") == "agent": - # Check for initial task creation message - if "Hello! I've received your task" in content.get("content", ""): - task_creation_found = True # Check for agent response to user message - elif "Hello! I've received your message" in content.get("content", ""): + if "Hello! I've received your message" in content.get("content", ""): # Agent response should come after user echo assert user_echo_found, "Agent response arrived before user message echo (incorrect order)" agent_response_found = True @@ -150,7 +163,7 @@ async def collect_stream_events(): user_echo_found = True # Exit early if we've found all expected messages - if task_creation_found and user_echo_found and agent_response_found: + if user_echo_found and agent_response_found: break # Start streaming task @@ -160,8 +173,15 @@ async def collect_stream_events(): event_content = TextContentParam(type="text", author="user", content=user_message) await client.agents.send_event(agent_id=agent_id, params={"task_id": task.id, "content": event_content}) - # Wait for streaming to complete - await stream_task + # Wait for the stream to complete (with timeout) + try: + await asyncio.wait_for(stream_task, timeout=stream_timeout) + except asyncio.TimeoutError: + pytest.fail(f"Stream timed out after {stream_timeout}s waiting for expected messages") + + # Verify all expected messages were received (fail if stream ended without finding them) + assert user_echo_found, "User message echo not found in stream" + assert agent_response_found, "Agent response not found in stream" if __name__ == "__main__": diff --git a/examples/tutorials/10_async/00_base/010_multiturn/Dockerfile b/examples/tutorials/10_async/00_base/010_multiturn/Dockerfile index f3acc25b5..48969ad90 100644 --- a/examples/tutorials/10_async/00_base/010_multiturn/Dockerfile +++ b/examples/tutorials/10_async/00_base/010_multiturn/Dockerfile @@ -23,20 +23,29 @@ RUN uv pip install --system --upgrade pip setuptools wheel ENV UV_HTTP_TIMEOUT=1000 # Copy pyproject.toml and README.md to install dependencies -COPY 010_multiturn/pyproject.toml /app/010_multiturn/pyproject.toml -COPY 010_multiturn/README.md /app/010_multiturn/README.md +COPY 10_async/00_base/010_multiturn/pyproject.toml /app/010_multiturn/pyproject.toml +COPY 10_async/00_base/010_multiturn/README.md /app/010_multiturn/README.md WORKDIR /app/010_multiturn -COPY 010_multiturn/project /app/010_multiturn/project +COPY 10_async/00_base/010_multiturn/project /app/010_multiturn/project -# Install the required Python packages from pyproject.toml -RUN uv pip install --system . +# Copy the test files +COPY 10_async/00_base/010_multiturn/tests /app/010_multiturn/tests + +# Copy shared test utilities +COPY test_utils /app/test_utils + +# Install the required Python packages with dev dependencies (includes pytest) +RUN uv pip install --system .[dev] pytest-asyncio httpx WORKDIR /app/010_multiturn # Set environment variables ENV PYTHONPATH=/app +# Set test environment variables +ENV AGENT_NAME=ab010-multiturn + # Run the agent using uvicorn CMD ["uvicorn", "project.acp:acp", "--host", "0.0.0.0", "--port", "8000"] diff --git a/examples/tutorials/10_async/00_base/010_multiturn/manifest.yaml b/examples/tutorials/10_async/00_base/010_multiturn/manifest.yaml index 17df32318..5d21e78d5 100644 --- a/examples/tutorials/10_async/00_base/010_multiturn/manifest.yaml +++ b/examples/tutorials/10_async/00_base/010_multiturn/manifest.yaml @@ -15,24 +15,25 @@ build: context: # Root directory for the build context - root: ../ # Keep this as the default root + root: ../../../ # Up to tutorials level to include test_utils # Paths to include in the Docker build context # Must include: # - Your agent's directory (your custom agent code) # These paths are collected and sent to the Docker daemon for building include_paths: - - 010_multiturn + - 10_async/00_base/010_multiturn + - test_utils # Path to your agent's Dockerfile # This defines how your agent's image is built from the context # Relative to the root directory - dockerfile: 010_multiturn/Dockerfile + dockerfile: 10_async/00_base/010_multiturn/Dockerfile # Path to your agent's .dockerignore # Filters unnecessary files from the build context # Helps keep build context small and builds fast - dockerignore: 010_multiturn/.dockerignore + dockerignore: 10_async/00_base/010_multiturn/.dockerignore # Local Development Configuration diff --git a/examples/tutorials/10_async/00_base/010_multiturn/tests/test_agent.py b/examples/tutorials/10_async/00_base/010_multiturn/tests/test_agent.py index ce9ab4a41..4da1745c6 100644 --- a/examples/tutorials/10_async/00_base/010_multiturn/tests/test_agent.py +++ b/examples/tutorials/10_async/00_base/010_multiturn/tests/test_agent.py @@ -185,8 +185,11 @@ async def stream_messages(): event_content = TextContentParam(type="text", author="user", content=user_message) await client.agents.send_event(agent_id=agent_id, params={"task_id": task.id, "content": event_content}) - # Wait for streaming to complete - await stream_task + # Wait for streaming to complete (with timeout) + try: + await asyncio.wait_for(stream_task, timeout=15) + except asyncio.TimeoutError: + pytest.fail("Stream timed out after 15s waiting for expected messages") # Validate we received events assert len(all_events) > 0, "No events received in streaming response" diff --git a/examples/tutorials/10_async/00_base/020_streaming/Dockerfile b/examples/tutorials/10_async/00_base/020_streaming/Dockerfile index 5f593a994..447ca292d 100644 --- a/examples/tutorials/10_async/00_base/020_streaming/Dockerfile +++ b/examples/tutorials/10_async/00_base/020_streaming/Dockerfile @@ -23,18 +23,28 @@ RUN uv pip install --system --upgrade pip setuptools wheel ENV UV_HTTP_TIMEOUT=1000 # Copy pyproject.toml and README.md to install dependencies -COPY 020_streaming/pyproject.toml /app/020_streaming/pyproject.toml -COPY 020_streaming/README.md /app/020_streaming/README.md +COPY 10_async/00_base/020_streaming/pyproject.toml /app/020_streaming/pyproject.toml +COPY 10_async/00_base/020_streaming/README.md /app/020_streaming/README.md WORKDIR /app/020_streaming # Copy the project code -COPY 020_streaming/project /app/020_streaming/project +COPY 10_async/00_base/020_streaming/project /app/020_streaming/project + +# Copy the test files +COPY 10_async/00_base/020_streaming/tests /app/020_streaming/tests + +# Copy shared test utilities +COPY test_utils /app/test_utils + +# Install the required Python packages with dev dependencies (includes pytest) +RUN uv pip install --system .[dev] pytest-asyncio httpx -# Install the required Python packages from pyproject.toml -RUN uv pip install --system . # Set environment variables ENV PYTHONPATH=/app +# Set test environment variables +ENV AGENT_NAME=ab020-streaming + # Run the agent using uvicorn CMD ["uvicorn", "project.acp:acp", "--host", "0.0.0.0", "--port", "8000"] diff --git a/examples/tutorials/10_async/00_base/020_streaming/manifest.yaml b/examples/tutorials/10_async/00_base/020_streaming/manifest.yaml index 437756654..bd5673a6b 100644 --- a/examples/tutorials/10_async/00_base/020_streaming/manifest.yaml +++ b/examples/tutorials/10_async/00_base/020_streaming/manifest.yaml @@ -15,24 +15,25 @@ build: context: # Root directory for the build context - root: ../ # Keep this as the default root + root: ../../../ # Up to tutorials level to include test_utils # Paths to include in the Docker build context # Must include: # - Your agent's directory (your custom agent code) # These paths are collected and sent to the Docker daemon for building include_paths: - - 020_streaming + - 10_async/00_base/020_streaming + - test_utils # Path to your agent's Dockerfile # This defines how your agent's image is built from the context # Relative to the root directory - dockerfile: 020_streaming/Dockerfile + dockerfile: 10_async/00_base/020_streaming/Dockerfile # Path to your agent's .dockerignore # Filters unnecessary files from the build context # Helps keep build context small and builds fast - dockerignore: 020_streaming/.dockerignore + dockerignore: 10_async/00_base/020_streaming/.dockerignore # Local Development Configuration diff --git a/examples/tutorials/10_async/00_base/020_streaming/tests/test_agent.py b/examples/tutorials/10_async/00_base/020_streaming/tests/test_agent.py index 4cdff79a9..d863199cd 100644 --- a/examples/tutorials/10_async/00_base/020_streaming/tests/test_agent.py +++ b/examples/tutorials/10_async/00_base/020_streaming/tests/test_agent.py @@ -96,7 +96,9 @@ async def test_send_event_and_poll(self, client: AsyncAgentex, agent_id: str): user_message=user_message, timeout=30, sleep_interval=1.0, + yield_updates=False, ): + messages.append(message) assert len(messages) > 0 @@ -199,7 +201,7 @@ async def stream_messages() -> None: await asyncio.sleep(1) # wait for state to be updated states = await client.states.list(agent_id=agent_id, task_id=task.id) assert len(states) == 1 - state = states[0].state + state: dict[str, object] = states[0].state messages = state.get("messages", []) assert isinstance(messages, list) diff --git a/examples/tutorials/10_async/00_base/030_tracing/Dockerfile b/examples/tutorials/10_async/00_base/030_tracing/Dockerfile index 783993362..2aee7e1dd 100644 --- a/examples/tutorials/10_async/00_base/030_tracing/Dockerfile +++ b/examples/tutorials/10_async/00_base/030_tracing/Dockerfile @@ -23,19 +23,28 @@ RUN uv pip install --system --upgrade pip setuptools wheel ENV UV_HTTP_TIMEOUT=1000 # Copy pyproject.toml and README.md to install dependencies -COPY 030_tracing/pyproject.toml /app/030_tracing/pyproject.toml -COPY 030_tracing/README.md /app/030_tracing/README.md +COPY 10_async/00_base/030_tracing/pyproject.toml /app/030_tracing/pyproject.toml +COPY 10_async/00_base/030_tracing/README.md /app/030_tracing/README.md WORKDIR /app/030_tracing # Copy the project code -COPY 030_tracing/project /app/030_tracing/project +COPY 10_async/00_base/030_tracing/project /app/030_tracing/project -# Install the required Python packages from pyproject.toml -RUN uv pip install --system . +# Copy the test files +COPY 10_async/00_base/030_tracing/tests /app/030_tracing/tests + +# Copy shared test utilities +COPY test_utils /app/test_utils + +# Install the required Python packages with dev dependencies (includes pytest) +RUN uv pip install --system .[dev] pytest-asyncio httpx # Set environment variables ENV PYTHONPATH=/app +# Set test environment variables +ENV AGENT_NAME=ab030-tracing + # Run the agent using uvicorn CMD ["uvicorn", "project.acp:acp", "--host", "0.0.0.0", "--port", "8000"] diff --git a/examples/tutorials/10_async/00_base/030_tracing/manifest.yaml b/examples/tutorials/10_async/00_base/030_tracing/manifest.yaml index 84a415e26..3c9b2c147 100644 --- a/examples/tutorials/10_async/00_base/030_tracing/manifest.yaml +++ b/examples/tutorials/10_async/00_base/030_tracing/manifest.yaml @@ -15,24 +15,25 @@ build: context: # Root directory for the build context - root: ../ # Keep this as the default root + root: ../../../ # Up to tutorials level to include test_utils # Paths to include in the Docker build context # Must include: # - Your agent's directory (your custom agent code) # These paths are collected and sent to the Docker daemon for building include_paths: - - 030_tracing + - 10_async/00_base/030_tracing + - test_utils # Path to your agent's Dockerfile # This defines how your agent's image is built from the context # Relative to the root directory - dockerfile: 030_tracing/Dockerfile + dockerfile: 10_async/00_base/030_tracing/Dockerfile # Path to your agent's .dockerignore # Filters unnecessary files from the build context # Helps keep build context small and builds fast - dockerignore: 030_tracing/.dockerignore + dockerignore: 10_async/00_base/030_tracing/.dockerignore # Local Development Configuration diff --git a/examples/tutorials/10_async/00_base/040_other_sdks/Dockerfile b/examples/tutorials/10_async/00_base/040_other_sdks/Dockerfile index 5761d08a0..2e0ee6ef0 100644 --- a/examples/tutorials/10_async/00_base/040_other_sdks/Dockerfile +++ b/examples/tutorials/10_async/00_base/040_other_sdks/Dockerfile @@ -23,19 +23,28 @@ RUN uv pip install --system --upgrade pip setuptools wheel ENV UV_HTTP_TIMEOUT=1000 # Copy pyproject.toml and README.md to install dependencies -COPY 040_other_sdks/pyproject.toml /app/040_other_sdks/pyproject.toml -COPY 040_other_sdks/README.md /app/040_other_sdks/README.md +COPY 10_async/00_base/040_other_sdks/pyproject.toml /app/040_other_sdks/pyproject.toml +COPY 10_async/00_base/040_other_sdks/README.md /app/040_other_sdks/README.md WORKDIR /app/040_other_sdks # Copy the project code -COPY 040_other_sdks/project /app/040_other_sdks/project +COPY 10_async/00_base/040_other_sdks/project /app/040_other_sdks/project -# Install the required Python packages from pyproject.toml -RUN uv pip install --system . +# Copy the test files +COPY 10_async/00_base/040_other_sdks/tests /app/040_other_sdks/tests + +# Copy shared test utilities +COPY test_utils /app/test_utils + +# Install the required Python packages with dev dependencies (includes pytest) +RUN uv pip install --system .[dev] pytest-asyncio httpx # Set environment variables ENV PYTHONPATH=/app +# Set test environment variables +ENV AGENT_NAME=ab040-other-sdks + # Run the agent using uvicorn CMD ["uvicorn", "project.acp:acp", "--host", "0.0.0.0", "--port", "8000"] diff --git a/examples/tutorials/10_async/00_base/040_other_sdks/manifest.yaml b/examples/tutorials/10_async/00_base/040_other_sdks/manifest.yaml index 75609389f..8fd324c13 100644 --- a/examples/tutorials/10_async/00_base/040_other_sdks/manifest.yaml +++ b/examples/tutorials/10_async/00_base/040_other_sdks/manifest.yaml @@ -15,24 +15,25 @@ build: context: # Root directory for the build context - root: ../ # Keep this as the default root + root: ../../../ # Up to tutorials level to include test_utils # Paths to include in the Docker build context # Must include: # - Your agent's directory (your custom agent code) # These paths are collected and sent to the Docker daemon for building include_paths: - - 040_other_sdks + - 10_async/00_base/040_other_sdks + - test_utils # Path to your agent's Dockerfile # This defines how your agent's image is built from the context # Relative to the root directory - dockerfile: 040_other_sdks/Dockerfile + dockerfile: 10_async/00_base/040_other_sdks/Dockerfile # Path to your agent's .dockerignore # Filters unnecessary files from the build context # Helps keep build context small and builds fast - dockerignore: 040_other_sdks/.dockerignore + dockerignore: 10_async/00_base/040_other_sdks/.dockerignore # Local Development Configuration diff --git a/examples/tutorials/10_async/00_base/080_batch_events/Dockerfile b/examples/tutorials/10_async/00_base/080_batch_events/Dockerfile index 352fa5223..dbeccdfb9 100644 --- a/examples/tutorials/10_async/00_base/080_batch_events/Dockerfile +++ b/examples/tutorials/10_async/00_base/080_batch_events/Dockerfile @@ -23,26 +23,29 @@ RUN uv pip install --system --upgrade pip setuptools wheel ENV UV_HTTP_TIMEOUT=1000 # Copy pyproject.toml and README.md to install dependencies -COPY 080_batch_events/pyproject.toml /app/080_batch_events/pyproject.toml -COPY 080_batch_events/README.md /app/080_batch_events/README.md +COPY 10_async/00_base/080_batch_events/pyproject.toml /app/080_batch_events/pyproject.toml +COPY 10_async/00_base/080_batch_events/README.md /app/080_batch_events/README.md WORKDIR /app/080_batch_events # Copy the project code -COPY 080_batch_events/project /app/080_batch_events/project +COPY 10_async/00_base/080_batch_events/project /app/080_batch_events/project -# Copy test files -COPY 080_batch_events/test_*.py /app/080_batch_events/ +# Copy the test files +COPY 10_async/00_base/080_batch_events/tests /app/080_batch_events/tests -# Install the required Python packages from pyproject.toml -RUN uv pip install --system . +# Copy shared test utilities +COPY test_utils /app/test_utils -# Install pytest for running tests -RUN uv pip install --system pytest pytest-asyncio +# Install the required Python packages with dev dependencies (includes pytest) +RUN uv pip install --system .[dev] pytest-asyncio httpx WORKDIR /app/080_batch_events # Set environment variables ENV PYTHONPATH=/app +# Set test environment variables +ENV AGENT_NAME=ab080-batch-events + # Run the agent using uvicorn CMD ["uvicorn", "project.acp:acp", "--host", "0.0.0.0", "--port", "8000"] diff --git a/examples/tutorials/10_async/00_base/080_batch_events/manifest.yaml b/examples/tutorials/10_async/00_base/080_batch_events/manifest.yaml index fa5abb921..dd5f8cbdc 100644 --- a/examples/tutorials/10_async/00_base/080_batch_events/manifest.yaml +++ b/examples/tutorials/10_async/00_base/080_batch_events/manifest.yaml @@ -15,24 +15,25 @@ build: context: # Root directory for the build context - root: ../ # Keep this as the default root + root: ../../../ # Up to tutorials level to include test_utils # Paths to include in the Docker build context # Must include: # - Your agent's directory (your custom agent code) # These paths are collected and sent to the Docker daemon for building include_paths: - - 080_batch_events + - 10_async/00_base/080_batch_events + - test_utils # Path to your agent's Dockerfile # This defines how your agent's image is built from the context # Relative to the root directory - dockerfile: 080_batch_events/Dockerfile + dockerfile: 10_async/00_base/080_batch_events/Dockerfile # Path to your agent's .dockerignore # Filters unnecessary files from the build context # Helps keep build context small and builds fast - dockerignore: 080_batch_events/.dockerignore + dockerignore: 10_async/00_base/080_batch_events/.dockerignore # Local Development Configuration diff --git a/examples/tutorials/10_async/00_base/090_multi_agent_non_temporal/Dockerfile b/examples/tutorials/10_async/00_base/090_multi_agent_non_temporal/Dockerfile index 9eab59400..24ecf4484 100644 --- a/examples/tutorials/10_async/00_base/090_multi_agent_non_temporal/Dockerfile +++ b/examples/tutorials/10_async/00_base/090_multi_agent_non_temporal/Dockerfile @@ -24,16 +24,22 @@ RUN uv pip install --system --upgrade pip setuptools wheel ENV UV_HTTP_TIMEOUT=1000 # Copy pyproject.toml and README.md to install dependencies -COPY 090_multi_agent_non_temporal/pyproject.toml /app/090_multi_agent_non_temporal/pyproject.toml -COPY 090_multi_agent_non_temporal/README.md /app/090_multi_agent_non_temporal/README.md +COPY 10_async/00_base/090_multi_agent_non_temporal/pyproject.toml /app/090_multi_agent_non_temporal/pyproject.toml +COPY 10_async/00_base/090_multi_agent_non_temporal/README.md /app/090_multi_agent_non_temporal/README.md WORKDIR /app/090_multi_agent_non_temporal # Copy the project code -COPY 090_multi_agent_non_temporal/project /app/090_multi_agent_non_temporal/project +COPY 10_async/00_base/090_multi_agent_non_temporal/project /app/090_multi_agent_non_temporal/project -# Install the required Python packages from pyproject.toml -RUN uv pip install --system . +# Copy the test files +COPY 10_async/00_base/090_multi_agent_non_temporal/tests /app/090_multi_agent_non_temporal/tests + +# Copy shared test utilities +COPY test_utils /app/test_utils + +# Install the required Python packages with dev dependencies +RUN uv pip install --system .[dev] # Set environment variables ENV PYTHONPATH=/app @@ -41,5 +47,11 @@ ENV PYTHONPATH=/app ARG AGENT_FILE ARG PORT +# Set test environment variables +ENV AGENT_NAME=ab090-multi-agent-non-temporal + +# Note: AGENT_NAME can be overridden at runtime based on which agent is running +# (ab090-creator-agent, ab090-critic-agent, ab090-formatter-agent, or ab090-orchestrator-agent) + # Run the agent using uvicorn CMD uvicorn project.${AGENT_FILE%.*}:acp --host 0.0.0.0 --port ${PORT:-8000} diff --git a/examples/tutorials/10_async/00_base/090_multi_agent_non_temporal/tests/test_agent.py b/examples/tutorials/10_async/00_base/090_multi_agent_non_temporal/tests/test_agent.py index c9624ff41..d4c1dd7dd 100644 --- a/examples/tutorials/10_async/00_base/090_multi_agent_non_temporal/tests/test_agent.py +++ b/examples/tutorials/10_async/00_base/090_multi_agent_non_temporal/tests/test_agent.py @@ -18,8 +18,8 @@ import os import uuid -import pytest -import pytest_asyncio +# import pytest +# import pytest_asyncio from test_utils.async_utils import ( stream_agent_response, send_event_and_poll_yielding, diff --git a/examples/tutorials/10_async/10_temporal/000_hello_acp/Dockerfile b/examples/tutorials/10_async/10_temporal/000_hello_acp/Dockerfile index 3c8356649..e739eb4a8 100644 --- a/examples/tutorials/10_async/10_temporal/000_hello_acp/Dockerfile +++ b/examples/tutorials/10_async/10_temporal/000_hello_acp/Dockerfile @@ -29,16 +29,28 @@ RUN uv pip install --system --upgrade pip setuptools wheel ENV UV_HTTP_TIMEOUT=1000 # Copy pyproject.toml and README.md to install dependencies -COPY 000_hello_acp/pyproject.toml /app/000_hello_acp/pyproject.toml -COPY 000_hello_acp/README.md /app/000_hello_acp/README.md +COPY 10_async/10_temporal/000_hello_acp/pyproject.toml /app/000_hello_acp/pyproject.toml +COPY 10_async/10_temporal/000_hello_acp/README.md /app/000_hello_acp/README.md WORKDIR /app/000_hello_acp # Copy the project code -COPY 000_hello_acp/project /app/000_hello_acp/project +COPY 10_async/10_temporal/000_hello_acp/project /app/000_hello_acp/project -# Install the required Python packages from pyproject.toml -RUN uv pip install --system . +# Copy the test files +COPY 10_async/10_temporal/000_hello_acp/tests /app/000_hello_acp/tests + +# Copy shared test utilities +COPY test_utils /app/test_utils + +# Install the required Python packages with dev dependencies (includes pytest) +RUN uv pip install --system .[dev] pytest-asyncio httpx + +# Set environment variables +ENV PYTHONPATH=/app + +# Set test environment variables +ENV AGENT_NAME=at000-hello-acp # Run the ACP server using uvicorn CMD ["uvicorn", "project.acp:acp", "--host", "0.0.0.0", "--port", "8000"] diff --git a/examples/tutorials/10_async/10_temporal/000_hello_acp/manifest.yaml b/examples/tutorials/10_async/10_temporal/000_hello_acp/manifest.yaml index 37dc3bd15..e93fe8eca 100644 --- a/examples/tutorials/10_async/10_temporal/000_hello_acp/manifest.yaml +++ b/examples/tutorials/10_async/10_temporal/000_hello_acp/manifest.yaml @@ -15,24 +15,25 @@ build: context: # Root directory for the build context - root: ../ # Keep this as the default root + root: ../../../ # Up to tutorials level to include test_utils # Paths to include in the Docker build context # Must include: # - Your agent's directory (your custom agent code) # These paths are collected and sent to the Docker daemon for building include_paths: - - 000_hello_acp + - 10_async/10_temporal/000_hello_acp + - test_utils # Path to your agent's Dockerfile # This defines how your agent's image is built from the context # Relative to the root directory - dockerfile: 000_hello_acp/Dockerfile + dockerfile: 10_async/10_temporal/000_hello_acp/Dockerfile # Path to your agent's .dockerignore # Filters unnecessary files from the build context # Helps keep build context small and builds fast - dockerignore: 000_hello_acp/.dockerignore + dockerignore: 10_async/10_temporal/000_hello_acp/.dockerignore # Local Development Configuration diff --git a/examples/tutorials/10_async/10_temporal/000_hello_acp/tests/test_agent.py b/examples/tutorials/10_async/10_temporal/000_hello_acp/tests/test_agent.py index f3e88e32d..9150afaa2 100644 --- a/examples/tutorials/10_async/10_temporal/000_hello_acp/tests/test_agent.py +++ b/examples/tutorials/10_async/10_temporal/000_hello_acp/tests/test_agent.py @@ -18,6 +18,7 @@ import os import uuid import asyncio +from typing import Any import pytest import pytest_asyncio @@ -71,7 +72,7 @@ async def test_send_event_and_poll(self, client: AsyncAgentex, agent_id: str): task_response = await client.agents.create_task(agent_id, params=ParamsCreateTaskRequest(name=uuid.uuid1().hex)) task = task_response.result assert task is not None - + task_creation_found = False # Poll for the initial task creation message async for message in poll_messages( client=client, @@ -82,8 +83,10 @@ async def test_send_event_and_poll(self, client: AsyncAgentex, agent_id: str): assert isinstance(message, TaskMessage) if message.content and message.content.type == "text" and message.content.author == "agent": assert "Hello! I've received your task" in message.content.content + task_creation_found = True break - + + assert task_creation_found, "Task creation message not found in poll" await asyncio.sleep(1.5) # Send an event and poll for response user_message = "Hello, this is a test message!" @@ -110,23 +113,37 @@ async def test_send_event_and_stream(self, client: AsyncAgentex, agent_id: str): task = task_response.result assert task is not None + task_creation_found = False + async for message in poll_messages( + client=client, + task_id=task.id, + timeout=30, + sleep_interval=1.0, + ): + assert isinstance(message, TaskMessage) + if message.content and message.content.type == "text" and message.content.author == "agent": + assert "Hello! I've received your task" in message.content.content + task_creation_found = True + break + + assert task_creation_found, "Task creation message not found in poll" + user_message = "Hello, this is a test message!" # Collect events from stream - all_events = [] + all_events: list[dict[str, Any]] = [] # Flags to track what we've received - task_creation_found = False user_echo_found = False agent_response_found = False - + stream_timeout = 30 async def collect_stream_events(): #noqa: ANN101 - nonlocal task_creation_found, user_echo_found, agent_response_found + nonlocal user_echo_found, agent_response_found async for event in stream_agent_response( client=client, task_id=task.id, - timeout=30, + timeout=stream_timeout, ): # Check events as they arrive event_type = event.get("type") @@ -135,11 +152,8 @@ async def collect_stream_events(): #noqa: ANN101 if content.get("content") is None: continue # Skip empty content if content.get("type") == "text" and content.get("author") == "agent": - # Check for initial task creation message - if "Hello! I've received your task" in content.get("content", ""): - task_creation_found = True # Check for agent response to user message - elif "Hello! I've received your message" in content.get("content", ""): + if "Hello! I've received your message" in content.get("content", ""): # Agent response should come after user echo assert user_echo_found, "Agent response arrived before user message echo (incorrect order)" agent_response_found = True @@ -149,14 +163,8 @@ async def collect_stream_events(): #noqa: ANN101 user_echo_found = True # Exit early if we've found all expected messages - if task_creation_found and user_echo_found and agent_response_found: + if user_echo_found and agent_response_found: break - - assert task_creation_found, "Task creation message not found in stream" - assert user_echo_found, "User message echo not found in stream" - assert agent_response_found, "Agent response not found in stream" - - # Start streaming task stream_task = asyncio.create_task(collect_stream_events()) @@ -164,6 +172,16 @@ async def collect_stream_events(): #noqa: ANN101 event_content = TextContentParam(type="text", author="user", content=user_message) await client.agents.send_event(agent_id=agent_id, params={"task_id": task.id, "content": event_content}) + # Wait for the stream to complete (with timeout) + try: + await asyncio.wait_for(stream_task, timeout=stream_timeout) + except asyncio.TimeoutError: + pytest.fail(f"Stream timed out after {stream_timeout}s waiting for expected messages") + + # Verify all expected messages were received (fail if stream ended without finding them) + + assert user_echo_found, "User message echo not found in stream" + assert agent_response_found, "Agent response not found in stream" # Wait for streaming to complete await stream_task diff --git a/examples/tutorials/10_async/10_temporal/010_agent_chat/Dockerfile b/examples/tutorials/10_async/10_temporal/010_agent_chat/Dockerfile index 16772e4a6..5ecf911b0 100644 --- a/examples/tutorials/10_async/10_temporal/010_agent_chat/Dockerfile +++ b/examples/tutorials/10_async/10_temporal/010_agent_chat/Dockerfile @@ -29,16 +29,28 @@ RUN uv pip install --system --upgrade pip setuptools wheel ENV UV_HTTP_TIMEOUT=1000 # Copy pyproject.toml and README.md to install dependencies -COPY 010_agent_chat/pyproject.toml /app/010_agent_chat/pyproject.toml -COPY 010_agent_chat/README.md /app/010_agent_chat/README.md +COPY 10_async/10_temporal/010_agent_chat/pyproject.toml /app/010_agent_chat/pyproject.toml +COPY 10_async/10_temporal/010_agent_chat/README.md /app/010_agent_chat/README.md WORKDIR /app/010_agent_chat # Copy the project code -COPY 010_agent_chat/project /app/010_agent_chat/project +COPY 10_async/10_temporal/010_agent_chat/project /app/010_agent_chat/project -# Install the required Python packages from pyproject.toml -RUN uv pip install --system . +# Copy the test files +COPY 10_async/10_temporal/010_agent_chat/tests /app/010_agent_chat/tests + +# Copy shared test utilities +COPY test_utils /app/test_utils + +# Install the required Python packages with dev dependencies (includes pytest) +RUN uv pip install --system .[dev] pytest-asyncio httpx + +# Set environment variables +ENV PYTHONPATH=/app + +# Set test environment variables +ENV AGENT_NAME=at010-agent-chat # Run the ACP server using uvicorn CMD ["uvicorn", "project.acp:acp", "--host", "0.0.0.0", "--port", "8000"] diff --git a/examples/tutorials/10_async/10_temporal/010_agent_chat/manifest.yaml b/examples/tutorials/10_async/10_temporal/010_agent_chat/manifest.yaml index 7a36311a3..1d53a7c2b 100644 --- a/examples/tutorials/10_async/10_temporal/010_agent_chat/manifest.yaml +++ b/examples/tutorials/10_async/10_temporal/010_agent_chat/manifest.yaml @@ -15,24 +15,25 @@ build: context: # Root directory for the build context - root: ../ # Keep this as the default root + root: ../../../ # Up to tutorials level to include test_utils # Paths to include in the Docker build context # Must include: # - Your agent's directory (your custom agent code) # These paths are collected and sent to the Docker daemon for building include_paths: - - 010_agent_chat + - 10_async/10_temporal/010_agent_chat + - test_utils # Path to your agent's Dockerfile # This defines how your agent's image is built from the context # Relative to the root directory - dockerfile: 010_agent_chat/Dockerfile + dockerfile: 10_async/10_temporal/010_agent_chat/Dockerfile # Path to your agent's .dockerignore # Filters unnecessary files from the build context # Helps keep build context small and builds fast - dockerignore: 010_agent_chat/.dockerignore + dockerignore: 10_async/10_temporal/010_agent_chat/.dockerignore # Local Development Configuration diff --git a/examples/tutorials/10_async/10_temporal/010_agent_chat/project/workflow.py b/examples/tutorials/10_async/10_temporal/010_agent_chat/project/workflow.py index ed2ec85be..3e3ac5b27 100644 --- a/examples/tutorials/10_async/10_temporal/010_agent_chat/project/workflow.py +++ b/examples/tutorials/10_async/10_temporal/010_agent_chat/project/workflow.py @@ -234,7 +234,7 @@ async def on_task_event_send(self, params: SendEventParams) -> None: "to provide accurate and well-reasoned responses." ), parent_span_id=span.id if span else None, - model="gpt-4o-mini", + model="gpt-5", model_settings=ModelSettings( # Include reasoning items in the response (IDs, summaries) # response_include=["reasoning.encrypted_content"], diff --git a/examples/tutorials/10_async/10_temporal/010_agent_chat/tests/test_agent.py b/examples/tutorials/10_async/10_temporal/010_agent_chat/tests/test_agent.py index 2710b9097..6eb03f728 100644 --- a/examples/tutorials/10_async/10_temporal/010_agent_chat/tests/test_agent.py +++ b/examples/tutorials/10_async/10_temporal/010_agent_chat/tests/test_agent.py @@ -156,7 +156,7 @@ async def test_multi_turn_conversation(self, client: AsyncAgentex, agent_id: str agent_id=agent_id, task_id=task.id, user_message=user_message_1, - timeout=20, + timeout=30, sleep_interval=1.0, ): assert isinstance(message, TaskMessage) @@ -215,13 +215,14 @@ async def test_send_event_and_stream_with_reasoning(self, client: AsyncAgentex, # Check for user message and agent response user_message_found = False agent_response_found = False + reasoning_found = False async def stream_messages() -> None: # noqa: ANN101 - nonlocal user_message_found, agent_response_found + nonlocal user_message_found, agent_response_found, reasoning_found async for event in stream_agent_response( client=client, task_id=task.id, - timeout=60, + timeout=90, # Increased timeout for CI environments ): msg_type = event.get("type") if msg_type == "full": @@ -241,22 +242,40 @@ async def stream_messages() -> None: # noqa: ANN101 ): agent_response_found = True elif finished_message.content and finished_message.content.type == "reasoning": - tool_response_found = True + reasoning_found = True + + # Exit early if we have what we need + if user_message_found and agent_response_found: + break + elif msg_type == "done": task_message_update = StreamTaskMessageDone.model_validate(event) if task_message_update.parent_task_message and task_message_update.parent_task_message.id: finished_message = await client.messages.retrieve(task_message_update.parent_task_message.id) if finished_message.content and finished_message.content.type == "reasoning": + reasoning_found = True + elif ( + finished_message.content + and finished_message.content.type == "text" + and finished_message.content.author == "agent" + ): agent_response_found = True - continue + + # Exit early if we have what we need + if user_message_found and agent_response_found: + break stream_task = asyncio.create_task(stream_messages()) event_content = TextContentParam(type="text", author="user", content=user_message) await client.agents.send_event(agent_id=agent_id, params={"task_id": task.id, "content": event_content}) - # Wait for streaming to complete - await stream_task + # Wait for streaming to complete with timeout + try: + await asyncio.wait_for(stream_task, timeout=120) # Overall timeout for CI + except asyncio.TimeoutError: + stream_task.cancel() + pytest.fail("Test timed out waiting for streaming response") assert user_message_found, "User message not found in stream" assert agent_response_found, "Agent response not found in stream" diff --git a/examples/tutorials/10_async/10_temporal/020_state_machine/Dockerfile b/examples/tutorials/10_async/10_temporal/020_state_machine/Dockerfile index 002100c0c..59051b4b8 100644 --- a/examples/tutorials/10_async/10_temporal/020_state_machine/Dockerfile +++ b/examples/tutorials/10_async/10_temporal/020_state_machine/Dockerfile @@ -29,21 +29,30 @@ RUN uv pip install --system --upgrade pip setuptools wheel ENV UV_HTTP_TIMEOUT=1000 # Copy pyproject.toml and README.md to install dependencies -COPY 020_state_machine/pyproject.toml /app/020_state_machine/pyproject.toml -COPY 020_state_machine/README.md /app/020_state_machine/README.md +COPY 10_async/10_temporal/020_state_machine/pyproject.toml /app/020_state_machine/pyproject.toml +COPY 10_async/10_temporal/020_state_machine/README.md /app/020_state_machine/README.md WORKDIR /app/020_state_machine # Copy the project code -COPY 020_state_machine/project /app/020_state_machine/project +COPY 10_async/10_temporal/020_state_machine/project /app/020_state_machine/project -# Install the required Python packages from pyproject.toml -RUN uv pip install --system . +# Copy the test files +COPY 10_async/10_temporal/020_state_machine/tests /app/020_state_machine/tests + +# Copy shared test utilities +COPY test_utils /app/test_utils + +# Install the required Python packages with dev dependencies (includes pytest) +RUN uv pip install --system .[dev] pytest-asyncio httpx WORKDIR /app/020_state_machine ENV PYTHONPATH=/app +# Set test environment variables +ENV AGENT_NAME=at020-state-machine + # Run the ACP server using uvicorn CMD ["uvicorn", "project.acp:acp", "--host", "0.0.0.0", "--port", "8000"] diff --git a/examples/tutorials/10_async/10_temporal/020_state_machine/manifest.yaml b/examples/tutorials/10_async/10_temporal/020_state_machine/manifest.yaml index 892df29f4..8b2bca147 100644 --- a/examples/tutorials/10_async/10_temporal/020_state_machine/manifest.yaml +++ b/examples/tutorials/10_async/10_temporal/020_state_machine/manifest.yaml @@ -15,24 +15,25 @@ build: context: # Root directory for the build context - root: ../ # Keep this as the default root + root: ../../../ # Up to tutorials level to include test_utils # Paths to include in the Docker build context # Must include: # - Your agent's directory (your custom agent code) # These paths are collected and sent to the Docker daemon for building include_paths: - - 020_state_machine + - 10_async/10_temporal/020_state_machine + - test_utils # Path to your agent's Dockerfile # This defines how your agent's image is built from the context # Relative to the root directory - dockerfile: 020_state_machine/Dockerfile + dockerfile: 10_async/10_temporal/020_state_machine/Dockerfile # Path to your agent's .dockerignore # Filters unnecessary files from the build context # Helps keep build context small and builds fast - dockerignore: 020_state_machine/.dockerignore + dockerignore: 10_async/10_temporal/020_state_machine/.dockerignore # Local Development Configuration diff --git a/examples/tutorials/10_async/10_temporal/030_custom_activities/Dockerfile b/examples/tutorials/10_async/10_temporal/030_custom_activities/Dockerfile index e631450f5..752ad8e93 100644 --- a/examples/tutorials/10_async/10_temporal/030_custom_activities/Dockerfile +++ b/examples/tutorials/10_async/10_temporal/030_custom_activities/Dockerfile @@ -29,16 +29,28 @@ RUN uv pip install --system --upgrade pip setuptools wheel ENV UV_HTTP_TIMEOUT=1000 # Copy pyproject.toml and README.md to install dependencies -COPY 030_custom_activities/pyproject.toml /app/030_custom_activities/pyproject.toml -COPY 030_custom_activities/README.md /app/030_custom_activities/README.md +COPY 10_async/10_temporal/030_custom_activities/pyproject.toml /app/030_custom_activities/pyproject.toml +COPY 10_async/10_temporal/030_custom_activities/README.md /app/030_custom_activities/README.md WORKDIR /app/030_custom_activities # Copy the project code -COPY 030_custom_activities/project /app/030_custom_activities/project +COPY 10_async/10_temporal/030_custom_activities/project /app/030_custom_activities/project -# Install the required Python packages from pyproject.toml -RUN uv pip install --system . +# Copy the test files +COPY 10_async/10_temporal/030_custom_activities/tests /app/030_custom_activities/tests + +# Copy shared test utilities +COPY test_utils /app/test_utils + +# Install the required Python packages with dev dependencies (includes pytest) +RUN uv pip install --system .[dev] pytest-asyncio httpx + +# Set environment variables +ENV PYTHONPATH=/app + +# Set test environment variables +ENV AGENT_NAME=at030-custom-activities # Run the ACP server using uvicorn CMD ["uvicorn", "project.acp:acp", "--host", "0.0.0.0", "--port", "8000"] diff --git a/examples/tutorials/10_async/10_temporal/030_custom_activities/manifest.yaml b/examples/tutorials/10_async/10_temporal/030_custom_activities/manifest.yaml index b81eff358..40af196f1 100644 --- a/examples/tutorials/10_async/10_temporal/030_custom_activities/manifest.yaml +++ b/examples/tutorials/10_async/10_temporal/030_custom_activities/manifest.yaml @@ -15,24 +15,25 @@ build: context: # Root directory for the build context - root: ../ # Keep this as the default root + root: ../../../ # Up to tutorials level to include test_utils # Paths to include in the Docker build context # Must include: # - Your agent's directory (your custom agent code) # These paths are collected and sent to the Docker daemon for building include_paths: - - 030_custom_activities + - 10_async/10_temporal/030_custom_activities + - test_utils # Path to your agent's Dockerfile # This defines how your agent's image is built from the context # Relative to the root directory - dockerfile: 030_custom_activities/Dockerfile + dockerfile: 10_async/10_temporal/030_custom_activities/Dockerfile # Path to your agent's .dockerignore # Filters unnecessary files from the build context # Helps keep build context small and builds fast - dockerignore: 030_custom_activities/.dockerignore + dockerignore: 10_async/10_temporal/030_custom_activities/.dockerignore # Local Development Configuration diff --git a/examples/tutorials/10_async/10_temporal/050_agent_chat_guardrails/Dockerfile b/examples/tutorials/10_async/10_temporal/050_agent_chat_guardrails/Dockerfile index 5f3f35c43..ef1ea0bf6 100644 --- a/examples/tutorials/10_async/10_temporal/050_agent_chat_guardrails/Dockerfile +++ b/examples/tutorials/10_async/10_temporal/050_agent_chat_guardrails/Dockerfile @@ -29,16 +29,28 @@ RUN uv pip install --system --upgrade pip setuptools wheel ENV UV_HTTP_TIMEOUT=1000 # Copy pyproject.toml and README.md to install dependencies -COPY 050_agent_chat_guardrails/pyproject.toml /app/050_agent_chat_guardrails/pyproject.toml -COPY 050_agent_chat_guardrails/README.md /app/050_agent_chat_guardrails/README.md +COPY 10_async/10_temporal/050_agent_chat_guardrails/pyproject.toml /app/050_agent_chat_guardrails/pyproject.toml +COPY 10_async/10_temporal/050_agent_chat_guardrails/README.md /app/050_agent_chat_guardrails/README.md WORKDIR /app/050_agent_chat_guardrails # Copy the project code -COPY 050_agent_chat_guardrails/project /app/050_agent_chat_guardrails/project +COPY 10_async/10_temporal/050_agent_chat_guardrails/project /app/050_agent_chat_guardrails/project -# Install the required Python packages from pyproject.toml -RUN uv pip install --system . +# Copy the test files +COPY 10_async/10_temporal/050_agent_chat_guardrails/tests /app/050_agent_chat_guardrails/tests + +# Copy shared test utilities +COPY test_utils /app/test_utils + +# Install the required Python packages with dev dependencies (includes pytest) +RUN uv pip install --system .[dev] pytest-asyncio httpx + +# Set environment variables +ENV PYTHONPATH=/app + +# Set test environment variables +ENV AGENT_NAME=at050-agent-chat-guardrails # Run the ACP server using uvicorn CMD ["uvicorn", "project.acp:acp", "--host", "0.0.0.0", "--port", "8000"] diff --git a/examples/tutorials/10_async/10_temporal/050_agent_chat_guardrails/manifest.yaml b/examples/tutorials/10_async/10_temporal/050_agent_chat_guardrails/manifest.yaml index 3f4d9309e..3fe94a001 100644 --- a/examples/tutorials/10_async/10_temporal/050_agent_chat_guardrails/manifest.yaml +++ b/examples/tutorials/10_async/10_temporal/050_agent_chat_guardrails/manifest.yaml @@ -15,24 +15,25 @@ build: context: # Root directory for the build context - root: ../ # Keep this as the default root + root: ../../../ # Up to tutorials level to include test_utils # Paths to include in the Docker build context # Must include: # - Your agent's directory (your custom agent code) # These paths are collected and sent to the Docker daemon for building include_paths: - - 050_agent_chat_guardrails + - 10_async/10_temporal/050_agent_chat_guardrails + - test_utils # Path to your agent's Dockerfile # This defines how your agent's image is built from the context # Relative to the root directory - dockerfile: 050_agent_chat_guardrails/Dockerfile + dockerfile: 10_async/10_temporal/050_agent_chat_guardrails/Dockerfile # Path to your agent's .dockerignore # Filters unnecessary files from the build context # Helps keep build context small and builds fast - dockerignore: 050_agent_chat_guardrails/.dockerignore + dockerignore: 10_async/10_temporal/050_agent_chat_guardrails/.dockerignore # Local Development Configuration diff --git a/examples/tutorials/10_async/10_temporal/060_open_ai_agents_sdk_hello_world/Dockerfile b/examples/tutorials/10_async/10_temporal/060_open_ai_agents_sdk_hello_world/Dockerfile index f5a592ebc..d38075e55 100644 --- a/examples/tutorials/10_async/10_temporal/060_open_ai_agents_sdk_hello_world/Dockerfile +++ b/examples/tutorials/10_async/10_temporal/060_open_ai_agents_sdk_hello_world/Dockerfile @@ -31,21 +31,30 @@ RUN uv pip install --system --upgrade pip setuptools wheel ENV UV_HTTP_TIMEOUT=1000 # Copy pyproject.toml and README.md to install dependencies -COPY 060_open_ai_agents_sdk_hello_world/pyproject.toml /app/060_open_ai_agents_sdk_hello_world/pyproject.toml -COPY 060_open_ai_agents_sdk_hello_world/README.md /app/060_open_ai_agents_sdk_hello_world/README.md +COPY 10_async/10_temporal/060_open_ai_agents_sdk_hello_world/pyproject.toml /app/060_open_ai_agents_sdk_hello_world/pyproject.toml +COPY 10_async/10_temporal/060_open_ai_agents_sdk_hello_world/README.md /app/060_open_ai_agents_sdk_hello_world/README.md WORKDIR /app/060_open_ai_agents_sdk_hello_world # Copy the project code -COPY 060_open_ai_agents_sdk_hello_world/project /app/060_open_ai_agents_sdk_hello_world/project +COPY 10_async/10_temporal/060_open_ai_agents_sdk_hello_world/project /app/060_open_ai_agents_sdk_hello_world/project -# Install the required Python packages from pyproject.toml -RUN uv pip install --system . +# Copy the test files +COPY 10_async/10_temporal/060_open_ai_agents_sdk_hello_world/tests /app/060_open_ai_agents_sdk_hello_world/tests -WORKDIR /app/060_open_ai_agents_sdk_hello_world +# Copy shared test utilities +COPY test_utils /app/test_utils + +# Install the required Python packages with dev dependencies +RUN uv pip install --system .[dev] +WORKDIR /app/060_open_ai_agents_sdk_hello_world ENV PYTHONPATH=/app + +# Set test environment variables +ENV AGENT_NAME=at060-open-ai-agents-sdk-hello-world + # Run the ACP server using uvicorn CMD ["uvicorn", "project.acp:acp", "--host", "0.0.0.0", "--port", "8000"] diff --git a/examples/tutorials/10_async/10_temporal/060_open_ai_agents_sdk_hello_world/manifest.yaml b/examples/tutorials/10_async/10_temporal/060_open_ai_agents_sdk_hello_world/manifest.yaml index e515c3b10..773d5ba44 100644 --- a/examples/tutorials/10_async/10_temporal/060_open_ai_agents_sdk_hello_world/manifest.yaml +++ b/examples/tutorials/10_async/10_temporal/060_open_ai_agents_sdk_hello_world/manifest.yaml @@ -15,24 +15,25 @@ build: context: # Root directory for the build context - root: ../ # Keep this as the default root + root: ../../../ # Up to tutorials level to include test_utils # Paths to include in the Docker build context # Must include: # - Your agent's directory (your custom agent code) # These paths are collected and sent to the Docker daemon for building include_paths: - - 060_open_ai_agents_sdk_hello_world + - 10_async/10_temporal/060_open_ai_agents_sdk_hello_world + - test_utils # Path to your agent's Dockerfile # This defines how your agent's image is built from the context # Relative to the root directory - dockerfile: 060_open_ai_agents_sdk_hello_world/Dockerfile + dockerfile: 10_async/10_temporal/060_open_ai_agents_sdk_hello_world/Dockerfile # Path to your agent's .dockerignore # Filters unnecessary files from the build context # Helps keep build context small and builds fast - dockerignore: 060_open_ai_agents_sdk_hello_world/.dockerignore + dockerignore: 10_async/10_temporal/060_open_ai_agents_sdk_hello_world/.dockerignore # Local Development Configuration diff --git a/examples/tutorials/10_async/10_temporal/070_open_ai_agents_sdk_tools/Dockerfile b/examples/tutorials/10_async/10_temporal/070_open_ai_agents_sdk_tools/Dockerfile index 68650d6f9..d4b343603 100644 --- a/examples/tutorials/10_async/10_temporal/070_open_ai_agents_sdk_tools/Dockerfile +++ b/examples/tutorials/10_async/10_temporal/070_open_ai_agents_sdk_tools/Dockerfile @@ -31,19 +31,31 @@ RUN uv pip install --system --upgrade pip setuptools wheel ENV UV_HTTP_TIMEOUT=1000 # Copy pyproject.toml and README.md to install dependencies -COPY 070_open_ai_agents_sdk_tools/pyproject.toml /app/070_open_ai_agents_sdk_tools/pyproject.toml -COPY 070_open_ai_agents_sdk_tools/README.md /app/070_open_ai_agents_sdk_tools/README.md +COPY 10_async/10_temporal/070_open_ai_agents_sdk_tools/pyproject.toml /app/070_open_ai_agents_sdk_tools/pyproject.toml +COPY 10_async/10_temporal/070_open_ai_agents_sdk_tools/README.md /app/070_open_ai_agents_sdk_tools/README.md WORKDIR /app/070_open_ai_agents_sdk_tools # Copy the project code -COPY 070_open_ai_agents_sdk_tools/project /app/070_open_ai_agents_sdk_tools/project +COPY 10_async/10_temporal/070_open_ai_agents_sdk_tools/project /app/070_open_ai_agents_sdk_tools/project -# Install the required Python packages from pyproject.toml -RUN uv pip install --system . +# Copy the test files +COPY 10_async/10_temporal/070_open_ai_agents_sdk_tools/tests /app/070_open_ai_agents_sdk_tools/tests + +# Copy shared test utilities +COPY test_utils /app/test_utils + +# Install the required Python packages with dev dependencies +RUN uv pip install --system .[dev] WORKDIR /app/070_open_ai_agents_sdk_tools +# Set environment variables +ENV PYTHONPATH=/app + +# Set test environment variables +ENV AGENT_NAME=at070-open-ai-agents-sdk-tools + # Run the ACP server using uvicorn CMD ["uvicorn", "project.acp:acp", "--host", "0.0.0.0", "--port", "8000"] diff --git a/examples/tutorials/10_async/10_temporal/070_open_ai_agents_sdk_tools/manifest.yaml b/examples/tutorials/10_async/10_temporal/070_open_ai_agents_sdk_tools/manifest.yaml index 265b08f65..40bf9e78a 100644 --- a/examples/tutorials/10_async/10_temporal/070_open_ai_agents_sdk_tools/manifest.yaml +++ b/examples/tutorials/10_async/10_temporal/070_open_ai_agents_sdk_tools/manifest.yaml @@ -15,24 +15,25 @@ build: context: # Root directory for the build context - root: ../ # Keep this as the default root + root: ../../../ # Up to tutorials level to include test_utils # Paths to include in the Docker build context # Must include: # - Your agent's directory (your custom agent code) # These paths are collected and sent to the Docker daemon for building include_paths: - - 070_open_ai_agents_sdk_tools + - 10_async/10_temporal/070_open_ai_agents_sdk_tools + - test_utils # Path to your agent's Dockerfile # This defines how your agent's image is built from the context # Relative to the root directory - dockerfile: 070_open_ai_agents_sdk_tools/Dockerfile + dockerfile: 10_async/10_temporal/070_open_ai_agents_sdk_tools/Dockerfile # Path to your agent's .dockerignore # Filters unnecessary files from the build context # Helps keep build context small and builds fast - dockerignore: 070_open_ai_agents_sdk_tools/.dockerignore + dockerignore: 10_async/10_temporal/070_open_ai_agents_sdk_tools/.dockerignore # Local Development Configuration # ----------------------------- diff --git a/examples/tutorials/10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/Dockerfile b/examples/tutorials/10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/Dockerfile index 4c21bd87b..cc4c06bf6 100644 --- a/examples/tutorials/10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/Dockerfile +++ b/examples/tutorials/10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/Dockerfile @@ -31,20 +31,30 @@ RUN uv pip install --system --upgrade pip setuptools wheel ENV UV_HTTP_TIMEOUT=1000 # Copy pyproject.toml and README.md to install dependencies -COPY 080_open_ai_agents_sdk_human_in_the_loop/pyproject.toml /app/080_open_ai_agents_sdk_human_in_the_loop/pyproject.toml -COPY 080_open_ai_agents_sdk_human_in_the_loop/README.md /app/080_open_ai_agents_sdk_human_in_the_loop/README.md +COPY 10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/pyproject.toml /app/080_open_ai_agents_sdk_human_in_the_loop/pyproject.toml +COPY 10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/README.md /app/080_open_ai_agents_sdk_human_in_the_loop/README.md WORKDIR /app/080_open_ai_agents_sdk_human_in_the_loop # Copy the project code -COPY 080_open_ai_agents_sdk_human_in_the_loop/project /app/080_open_ai_agents_sdk_human_in_the_loop/project +COPY 10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/project /app/080_open_ai_agents_sdk_human_in_the_loop/project -# Install the required Python packages from pyproject.toml -RUN uv pip install --system . +# Copy the test files +COPY 10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/tests /app/080_open_ai_agents_sdk_human_in_the_loop/tests + +# Copy shared test utilities +COPY test_utils /app/test_utils + +# Install the required Python packages with dev dependencies +RUN uv pip install --system .[dev] WORKDIR /app/080_open_ai_agents_sdk_human_in_the_loop ENV PYTHONPATH=/app + +# Set test environment variables +ENV AGENT_NAME=at080-open-ai-agents-sdk-human-in-the-loop + # Run the ACP server using uvicorn CMD ["uvicorn", "project.acp:acp", "--host", "0.0.0.0", "--port", "8000"] diff --git a/examples/tutorials/10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/manifest.yaml b/examples/tutorials/10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/manifest.yaml index d12ffe47d..09f49c3e5 100644 --- a/examples/tutorials/10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/manifest.yaml +++ b/examples/tutorials/10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/manifest.yaml @@ -15,24 +15,25 @@ build: context: # Root directory for the build context - root: ../ # Keep this as the default root + root: ../../../ # Up to tutorials level to include test_utils # Paths to include in the Docker build context # Must include: # - Your agent's directory (your custom agent code) # These paths are collected and sent to the Docker daemon for building include_paths: - - 080_open_ai_agents_sdk_human_in_the_loop + - 10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop + - test_utils # Path to your agent's Dockerfile # This defines how your agent's image is built from the context # Relative to the root directory - dockerfile: 080_open_ai_agents_sdk_human_in_the_loop/Dockerfile + dockerfile: 10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/Dockerfile # Path to your agent's .dockerignore # Filters unnecessary files from the build context # Helps keep build context small and builds fast - dockerignore: 080_open_ai_agents_sdk_human_in_the_loop/.dockerignore + dockerignore: 10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/.dockerignore # Local Development Configuration diff --git a/examples/tutorials/10_async/10_temporal/090_claude_agents_sdk_mvp/Dockerfile b/examples/tutorials/10_async/10_temporal/090_claude_agents_sdk_mvp/Dockerfile index 4699b8331..5428e814a 100644 --- a/examples/tutorials/10_async/10_temporal/090_claude_agents_sdk_mvp/Dockerfile +++ b/examples/tutorials/10_async/10_temporal/090_claude_agents_sdk_mvp/Dockerfile @@ -31,21 +31,30 @@ RUN uv pip install --system --upgrade pip setuptools wheel ENV UV_HTTP_TIMEOUT=1000 # Copy pyproject.toml and README.md to install dependencies -COPY 090_claude_agents_sdk_mvp/pyproject.toml /app/090_claude_agents_sdk_mvp/pyproject.toml -COPY 090_claude_agents_sdk_mvp/README.md /app/090_claude_agents_sdk_mvp/README.md +COPY 10_async/10_temporal/090_claude_agents_sdk_mvp/pyproject.toml /app/090_claude_agents_sdk_mvp/pyproject.toml +COPY 10_async/10_temporal/090_claude_agents_sdk_mvp/README.md /app/090_claude_agents_sdk_mvp/README.md WORKDIR /app/090_claude_agents_sdk_mvp # Copy the project code -COPY 090_claude_agents_sdk_mvp/project /app/090_claude_agents_sdk_mvp/project +COPY 10_async/10_temporal/090_claude_agents_sdk_mvp/project /app/090_claude_agents_sdk_mvp/project -# Install the required Python packages from pyproject.toml -RUN uv pip install --system . +# Copy the test files +COPY 10_async/10_temporal/090_claude_agents_sdk_mvp/tests /app/090_claude_agents_sdk_mvp/tests -WORKDIR /app/090_claude_agents_sdk_mvp +# Copy shared test utilities +COPY test_utils /app/test_utils + +# Install the required Python packages with dev dependencies +RUN uv pip install --system .[dev] +WORKDIR /app/090_claude_agents_sdk_mvp +# Set environment variables ENV PYTHONPATH=/app + +# Set test environment variables +ENV AGENT_NAME=at090-claude-agents-sdk-mvp # Run the ACP server using uvicorn CMD ["uvicorn", "project.acp:acp", "--host", "0.0.0.0", "--port", "8000"] diff --git a/examples/tutorials/10_async/10_temporal/090_claude_agents_sdk_mvp/manifest.yaml b/examples/tutorials/10_async/10_temporal/090_claude_agents_sdk_mvp/manifest.yaml index 3af65eede..2c1ce21dd 100644 --- a/examples/tutorials/10_async/10_temporal/090_claude_agents_sdk_mvp/manifest.yaml +++ b/examples/tutorials/10_async/10_temporal/090_claude_agents_sdk_mvp/manifest.yaml @@ -3,11 +3,26 @@ kind: Agent # Build Configuration build: context: - root: ../ + # Root directory for the build context + root: ../../../ # Up to tutorials level to include test_utils + + # Paths to include in the Docker build context + # Must include: + # - Your agent's directory (your custom agent code) + # These paths are collected and sent to the Docker daemon for building include_paths: - - 090_claude_agents_sdk_mvp - dockerfile: 090_claude_agents_sdk_mvp/Dockerfile - dockerignore: 090_claude_agents_sdk_mvp/.dockerignore + - 10_async/10_temporal/090_claude_agents_sdk_mvp + - test_utils + + # Path to your agent's Dockerfile + # This defines how your agent's image is built from the context + # Relative to the root directory + dockerfile: 10_async/10_temporal/090_claude_agents_sdk_mvp/Dockerfile + + # Path to your agent's .dockerignore + # Filters unnecessary files from the build context + # Helps keep build context small and builds fast + dockerignore: 10_async/10_temporal/090_claude_agents_sdk_mvp/.dockerignore # Local Development Configuration local_development: diff --git a/examples/tutorials/10_async/10_temporal/090_claude_agents_sdk_mvp/tests/test_agent.py b/examples/tutorials/10_async/10_temporal/090_claude_agents_sdk_mvp/tests/test_agent.py new file mode 100644 index 000000000..aae1cb916 --- /dev/null +++ b/examples/tutorials/10_async/10_temporal/090_claude_agents_sdk_mvp/tests/test_agent.py @@ -0,0 +1,67 @@ +import os + +# import uuid +# import asyncio +import pytest +import pytest_asyncio + +# from test_utils.async_utils import ( +# poll_messages, +# stream_agent_response, +# send_event_and_poll_yielding, +# ) +from agentex import AsyncAgentex + +# from agentex.types import TaskMessage +# from agentex.types.agent_rpc_params import ParamsCreateTaskRequest +# from agentex.types.text_content_param import TextContentParam + +# Configuration from environment variables +AGENTEX_API_BASE_URL = os.environ.get("AGENTEX_API_BASE_URL", "http://localhost:5003") +AGENT_NAME = os.environ.get("AGENT_NAME", "claude_") + + +@pytest_asyncio.fixture +async def client(): + """Create an AgentEx client instance for testing.""" + client = AsyncAgentex(base_url=AGENTEX_API_BASE_URL) + yield client + await client.close() + + +@pytest.fixture +def agent_name(): + """Return the agent name for testing.""" + return AGENT_NAME + + +@pytest_asyncio.fixture +async def agent_id(client: AsyncAgentex, agent_name): + """Retrieve the agent ID based on the agent name.""" + agents = await client.agents.list() + for agent in agents: + if agent.name == agent_name: + return agent.id + raise ValueError(f"Agent with name {agent_name} not found.") + + +class TestNonStreamingEvents: + """Test non-streaming event sending and polling.""" + + @pytest.mark.asyncio + async def test_send_event_and_poll(self, client: AsyncAgentex, agent_id: str): + """Test sending an event and polling for the response.""" + pass + + +class TestStreamingEvents: + """Test streaming event sending.""" + + @pytest.mark.asyncio + async def test_send_event_and_stream(self, client: AsyncAgentex, agent_id: str): + """Test sending an event and streaming the response.""" + pass + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/examples/tutorials/test_utils/async_utils.py b/examples/tutorials/test_utils/async_utils.py index d34054178..effe87789 100644 --- a/examples/tutorials/test_utils/async_utils.py +++ b/examples/tutorials/test_utils/async_utils.py @@ -125,8 +125,14 @@ async def poll_messages( if yield_updates: # For streaming: track content changes - content_str = message.content.content if message.content and hasattr(message.content, 'content') else "" - content_hash = hash(content_str + str(message.streaming_status)) + # Use getattr to safely extract content and convert to string + # This handles various content structures at runtime + raw_content = getattr(message.content, 'content', message.content) if message.content else None + content_str = str(raw_content) if raw_content is not None else "" + + # Ensure streaming_status is also properly converted to string + streaming_status_str = str(message.streaming_status) if message.streaming_status is not None else "" + content_hash = hash(content_str + streaming_status_str) is_updated = message.id in message_content_hashes and message_content_hashes[message.id] != content_hash if is_new_message or is_updated: @@ -218,6 +224,7 @@ async def stream_agent_response( except Exception as e: print(f"[DEBUG] Stream error: {e}") + async def stream_task_messages( client: AsyncAgentex, task_id: str, @@ -245,22 +252,3 @@ async def stream_task_messages( task_message = finished_message if task_message: yield task_message - - - -def validate_text_in_response(expected_text: str, message: TaskMessage) -> bool: - """ - Validate that expected text appears in any of the messages. - - Args: - expected_text: The text to search for (case-insensitive) - messages: List of message objects to search - - Returns: - True if text is found, False otherwise - """ - for message in messages: - if message.content and message.content.type == "text": - if expected_text.lower() in message.content.content.lower(): - return True - return False