Skip to content

Commit 4ab804d

Browse files
author
Charlotte Zhuang
committed
custom agent chat tutorial
1 parent df8429c commit 4ab804d

File tree

14 files changed

+3171
-0
lines changed

14 files changed

+3171
-0
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Python
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
*.so
6+
.Python
7+
build/
8+
develop-eggs/
9+
dist/
10+
downloads/
11+
eggs/
12+
.eggs/
13+
lib/
14+
lib64/
15+
parts/
16+
sdist/
17+
var/
18+
wheels/
19+
*.egg-info/
20+
.installed.cfg
21+
*.egg
22+
23+
# Environments
24+
.env**
25+
.venv
26+
env/
27+
venv/
28+
ENV/
29+
env.bak/
30+
venv.bak/
31+
32+
# IDE
33+
.idea/
34+
.vscode/
35+
*.swp
36+
*.swo
37+
38+
# Git
39+
.git
40+
.gitignore
41+
42+
# Misc
43+
.DS_Store
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# syntax=docker/dockerfile:1.3
2+
FROM python:3.12-slim
3+
COPY --from=ghcr.io/astral-sh/uv:0.6.4 /uv /uvx /bin/
4+
5+
# Install system dependencies
6+
RUN apt-get update && apt-get install -y \
7+
htop \
8+
vim \
9+
curl \
10+
tar \
11+
python3-dev \
12+
postgresql-client \
13+
build-essential \
14+
libpq-dev \
15+
gcc \
16+
cmake \
17+
netcat-openbsd \
18+
&& apt-get clean \
19+
&& rm -rf /var/lib/apt/lists/*
20+
21+
# Install tctl (Temporal CLI)
22+
RUN curl -L https://github.com/temporalio/tctl/releases/download/v1.18.1/tctl_1.18.1_linux_arm64.tar.gz -o /tmp/tctl.tar.gz && \
23+
tar -xzf /tmp/tctl.tar.gz -C /usr/local/bin && \
24+
chmod +x /usr/local/bin/tctl && \
25+
rm /tmp/tctl.tar.gz
26+
27+
RUN uv pip install --system --upgrade pip setuptools wheel
28+
29+
ENV UV_HTTP_TIMEOUT=1000
30+
31+
# Copy just the requirements file to optimize caching
32+
COPY 000_hello_acp/requirements.txt /app/requirements.txt
33+
34+
WORKDIR /app/
35+
36+
# Install the required Python packages
37+
RUN uv pip install --system -r requirements.txt
38+
39+
# Copy the project code
40+
COPY 000_hello_acp/project /app/project
41+
42+
WORKDIR /app/project
43+
44+
# Run the ACP server using uvicorn
45+
CMD ["uvicorn", "acp:acp", "--host", "0.0.0.0", "--port", "8000"]
46+
47+
# When we deploy the worker, we will replace the CMD with the following
48+
# CMD ["python", "-m", "run_worker"]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
dev:
2+
uv run --env-file=.env agentex agents run --manifest manifest.yaml
3+
4+
typecheck:
5+
uv run mypy project --strict
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# [Agentic] Agentic Chat with a Custom Temporal Activity
2+
3+
This tutorial demonstrates how to implement a custom temporal activity with agentic ACP.
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"id": "0",
7+
"metadata": {},
8+
"outputs": [],
9+
"source": [
10+
"from agentex import Agentex\n",
11+
"\n",
12+
"client = Agentex(base_url=\"http://localhost:5003\")"
13+
]
14+
},
15+
{
16+
"cell_type": "code",
17+
"execution_count": null,
18+
"id": "1",
19+
"metadata": {},
20+
"outputs": [],
21+
"source": [
22+
"AGENT_NAME = \"at011-custom-agent-chat\""
23+
]
24+
},
25+
{
26+
"cell_type": "code",
27+
"execution_count": null,
28+
"id": "2",
29+
"metadata": {},
30+
"outputs": [],
31+
"source": [
32+
"# (REQUIRED) Create a new task. For Agentic agents, you must create a task for messages to be associated with.\n",
33+
"import uuid\n",
34+
"\n",
35+
"rpc_response = client.agents.create_task(\n",
36+
" agent_name=AGENT_NAME,\n",
37+
" params={\n",
38+
" \"name\": f\"{str(uuid.uuid4())[:8]}-task\",\n",
39+
" \"params\": {}\n",
40+
" }\n",
41+
")\n",
42+
"\n",
43+
"task = rpc_response.result\n",
44+
"task"
45+
]
46+
},
47+
{
48+
"cell_type": "code",
49+
"execution_count": null,
50+
"id": "3",
51+
"metadata": {},
52+
"outputs": [],
53+
"source": [
54+
"# Send an event to the agent\n",
55+
"\n",
56+
"# The response is expected to be a list of TaskMessage objects, which is a union of the following types:\n",
57+
"# - TextContent: A message with just text content \n",
58+
"# - DataContent: A message with JSON-serializable data content\n",
59+
"# - ToolRequestContent: A message with a tool request, which contains a JSON-serializable request to call a tool\n",
60+
"# - ToolResponseContent: A message with a tool response, which contains response object from a tool call in its content\n",
61+
"\n",
62+
"# When processing the message/send response, if you are expecting more than TextContent, such as DataContent, ToolRequestContent, or ToolResponseContent, you can process them as well\n",
63+
"\n",
64+
"rpc_response = client.agents.send_event(\n",
65+
" agent_name=AGENT_NAME,\n",
66+
" params={\n",
67+
" \"content\": {\"type\": \"text\", \"author\": \"user\", \"content\": \"Hello what can you do?\"},\n",
68+
" \"task_id\": task.id,\n",
69+
" }\n",
70+
")\n",
71+
"\n",
72+
"event = rpc_response.result\n",
73+
"event"
74+
]
75+
},
76+
{
77+
"cell_type": "code",
78+
"execution_count": null,
79+
"id": "4",
80+
"metadata": {},
81+
"outputs": [],
82+
"source": [
83+
"# Subscribe to the async task messages produced by the agent\n",
84+
"from agentex.lib.utils.dev_tools import subscribe_to_async_task_messages\n",
85+
"\n",
86+
"task_messages = subscribe_to_async_task_messages(\n",
87+
" client=client,\n",
88+
" task=task, \n",
89+
" only_after_timestamp=event.created_at, \n",
90+
" print_messages=True,\n",
91+
" rich_print=True,\n",
92+
" timeout=5,\n",
93+
")"
94+
]
95+
},
96+
{
97+
"cell_type": "code",
98+
"execution_count": null,
99+
"id": "5",
100+
"metadata": {},
101+
"outputs": [],
102+
"source": []
103+
}
104+
],
105+
"metadata": {
106+
"kernelspec": {
107+
"display_name": "at001-custom-temporal-activity",
108+
"language": "python",
109+
"name": "python3"
110+
},
111+
"language_info": {
112+
"codemirror_mode": {
113+
"name": "ipython",
114+
"version": 3
115+
},
116+
"file_extension": ".py",
117+
"mimetype": "text/x-python",
118+
"name": "python",
119+
"nbconvert_exporter": "python",
120+
"pygments_lexer": "ipython3",
121+
"version": "3.13.5"
122+
}
123+
},
124+
"nbformat": 4,
125+
"nbformat_minor": 5
126+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# Agent Manifest Configuration
2+
# ---------------------------
3+
# This file defines how your agent should be built and deployed.
4+
5+
# Build Configuration
6+
# ------------------
7+
# The build config defines what gets packaged into your agent's Docker image.
8+
# This same configuration is used whether building locally or remotely.
9+
#
10+
# When building:
11+
# 1. All files from include_paths are collected into a build context
12+
# 2. The context is filtered by dockerignore rules
13+
# 3. The Dockerfile uses this context to build your agent's image
14+
# 4. The image is pushed to a registry and used to run your agent
15+
build:
16+
context:
17+
# Root directory for the build context
18+
root: ../ # Keep this as the default root
19+
20+
# Paths to include in the Docker build context
21+
# Must include:
22+
# - Your agent's directory (your custom agent code)
23+
# These paths are collected and sent to the Docker daemon for building
24+
include_paths:
25+
- 011_custom_agent_chat
26+
27+
# Path to your agent's Dockerfile
28+
# This defines how your agent's image is built from the context
29+
# Relative to the root directory
30+
dockerfile: 011_custom_agent_chat/Dockerfile
31+
32+
# Path to your agent's .dockerignore
33+
# Filters unnecessary files from the build context
34+
# Helps keep build context small and builds fast
35+
dockerignore: 011_custom_agent_chat/.dockerignore
36+
37+
# Local Development Configuration
38+
# -----------------------------
39+
# Only used when running the agent locally
40+
local_development:
41+
agent:
42+
port: 8000 # Port where your local ACP server is running
43+
host_address: host.docker.internal # Host address for Docker networking (host.docker.internal for Docker, localhost for direct)
44+
45+
# File paths for local development (relative to this manifest.yaml)
46+
paths:
47+
# Path to ACP server file
48+
# Examples:
49+
# project/acp.py (standard)
50+
# src/server.py (custom structure)
51+
# ../shared/acp.py (shared across projects)
52+
# /absolute/path/acp.py (absolute path)
53+
acp: project/acp.py
54+
55+
# Path to temporal worker file
56+
# Examples:
57+
# project/run_worker.py (standard)
58+
# workers/temporal.py (custom structure)
59+
# ../shared/worker.py (shared across projects)
60+
worker: project/run_worker.py
61+
62+
# Agent Configuration
63+
# -----------------
64+
agent:
65+
# Type of agent - either sync or agentic
66+
acp_type: agentic
67+
68+
# Unique name for your agent
69+
# Used for task routing and monitoring
70+
name: at011-custom-agent-chat
71+
72+
# Description of what your agent does
73+
# Helps with documentation and discovery
74+
description: An AgentEx agent that streams chat using a custom Temporal activity
75+
76+
# Temporal workflow configuration
77+
# This enables your agent to run as a Temporal workflow for long-running tasks
78+
temporal:
79+
enabled: true
80+
workflows:
81+
# Name of the workflow class
82+
# Must match the @workflow.defn name in your workflow.py
83+
- name: at011-custom-agent-chat
84+
85+
# Queue name for task distribution
86+
# Used by Temporal to route tasks to your agent
87+
# Convention: <agent_name>_task_queue
88+
queue_name: 011_custom_agent_chat_queue
89+
90+
# Optional: Credentials mapping
91+
# Maps Kubernetes secrets to environment variables
92+
# Common credentials include:
93+
# credentials:
94+
# - env_var_name: OPENAI_API_KEY
95+
# secret_name: openai-api-key
96+
# secret_key: api-key
97+
98+
# Optional: Set Environment variables for running your agent locally as well
99+
# as for deployment later on
100+
# env:
101+
# - name: OPENAI_BASE_URL
102+
# value: "https://api.openai.com/v1"
103+
# - name: ACCOUNT_ID
104+
# value: "your_account_id_here"
105+
106+
# Deployment Configuration
107+
# -----------------------
108+
# Configuration for deploying your agent to Kubernetes clusters
109+
deployment:
110+
# Container image configuration
111+
image:
112+
repository: "" # Update with your container registry
113+
tag: "latest" # Default tag, should be versioned in production
114+
115+
imagePullSecrets:
116+
- name: my-registry-secret # Update with your image pull secret name
117+
118+
# Global deployment settings that apply to all clusters
119+
# These can be overridden using --override-file with custom configuration files
120+
global:
121+
agent:
122+
name: "at011-custom-agent-chat"
123+
description: "An AgentEx agent that streams chat using a custom Temporal activity"
124+
125+
# Default replica count
126+
replicaCount: 1
127+
128+
# Default resource requirements
129+
resources:
130+
requests:
131+
cpu: "500m"
132+
memory: "1Gi"
133+
limits:
134+
cpu: "1000m"
135+
memory: "2Gi"

examples/tutorials/10_agentic/10_temporal/011_custom_agent_chat/project/__init__.py

Whitespace-only changes.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import os
2+
3+
from agentex.lib.types.fastacp import TemporalACPConfig
4+
from agentex.lib.sdk.fastacp.fastacp import FastACP
5+
6+
# Create the ACP server
7+
acp = FastACP.create(
8+
acp_type="agentic",
9+
config=TemporalACPConfig(
10+
# When deployed to the cluster, the Temporal address will automatically be set to the cluster address
11+
# For local development, we set the address manually to talk to the local Temporal service set up via docker compose
12+
type="temporal",
13+
temporal_address=os.getenv("TEMPORAL_ADDRESS", "localhost:7233")
14+
)
15+
)
16+
17+
18+
# Notice that we don't need to register any handlers when we use type="temporal"
19+
# If you look at the code in agentex.sdk.fastacp.impl.temporal_acp
20+
# You can see that these handlers are automatically registered when the ACP is created
21+
22+
# @acp.on_task_create
23+
# This will be handled by the method in your workflow that is decorated with @workflow.run
24+
25+
# @acp.on_task_event_send
26+
# This will be handled by the method in your workflow that is decorated with @workflow.signal(name=SignalName.RECEIVE_MESSAGE)
27+
28+
# @acp.on_task_cancel
29+
# This does not need to be handled by your workflow.
30+
# It is automatically handled by the temporal client which cancels the workflow directly

examples/tutorials/10_agentic/10_temporal/011_custom_agent_chat/project/py.typed

Whitespace-only changes.

0 commit comments

Comments
 (0)