Skip to content

Commit 974b406

Browse files
committed
feat(examples): add script to manage examples
Signed-off-by: Aleš Kalfas <kalfas.ales@gmail.com>
1 parent a18bfed commit 974b406

File tree

6 files changed

+264
-4
lines changed

6 files changed

+264
-4
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"name": "advanced-history-example",
9+
"type": "debugpy",
10+
"justMyCode": false,
11+
"request": "launch",
12+
"program": "${workspaceFolder}/src/advanced_history/agent.py",
13+
"console": "integratedTerminal"
14+
}
15+
]
16+
}

examples/advanced-history/src/advanced_history/agent.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def to_framework_message(message: Message) -> FrameworkMessage:
3838

3939

4040
@server.agent()
41-
async def multi_turn_chat_agent(
41+
async def advanced_history_example(
4242
input: Message,
4343
context: RunContext,
4444
llm: Annotated[LLMServiceExtensionServer, LLMServiceExtensionSpec.single_demand()],
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"name": "basic-history-example",
9+
"type": "debugpy",
10+
"justMyCode": false,
11+
"request": "launch",
12+
"program": "${workspaceFolder}/src/basic_history/agent.py",
13+
"console": "integratedTerminal"
14+
}
15+
]
16+
}

examples/basic-history/src/basic_history/agent.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414

1515
@server.agent()
16-
async def basic_history_agent(input: Message, context: RunContext):
16+
async def basic_history_example(input: Message, context: RunContext):
1717
"""Agent that demonstrates conversation history access"""
1818

1919
# Store the current message in the context store

examples/entrypoint.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/bin/bash
22
set -e
33

4-
# EXAMPLES: comma-separated list, e.g. "chat", "chat,rag", or "all"
4+
# EXAMPLES: comma-separated list, e.g. "basic-history", "basic-history,advanced-history", or "all"
55
EXAMPLES_INPUT="${EXAMPLES:-all}"
66

77
# Discover available examples
@@ -34,7 +34,7 @@ for example in "${EXAMPLE_LIST[@]}"; do
3434

3535
echo "Starting $example on port $PORT_BASE"
3636
cd "$example_dir"
37-
PORT=$PORT_BASE "$example_dir/.venv/bin/server" &
37+
PORT=$PORT_BASE HOST=0.0.0.0 "$example_dir/.venv/bin/server" &
3838
PIDS+=($!)
3939
((PORT_BASE++))
4040
done

examples/examples.sh

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
#!/bin/bash
2+
# Manage example agents lifecycle: build, run, and register with agentstack.
3+
#
4+
# This script provides a unified interface for:
5+
# - Building the examples Docker image
6+
# - Running example agents in a Docker container
7+
# - Registering/removing agents with the agentstack platform
8+
#
9+
# Usage:
10+
# ./examples.sh build - Build Docker image
11+
# ./examples.sh run - Run Docker container (foreground)
12+
# ./examples.sh add - Register agents with agentstack
13+
# ./examples.sh remove - Remove agents from agentstack
14+
# ./examples.sh start - Run Docker (background) and register agents
15+
# ./examples.sh stop - Stop the Docker container
16+
#
17+
# Example workflow:
18+
# ./examples.sh build # Build the Docker image
19+
# ./examples.sh start # Run container and register agents
20+
# ./examples.sh stop # Stop the container
21+
#
22+
# Environment variables:
23+
# EXAMPLES - Comma-separated list of examples, or "all" (default: all)
24+
# HOST - Agent host for registration (default: host.docker.internal)
25+
# PORT_BASE - Starting port number (default: 8001)
26+
# IMAGE - Docker image name (default: examples)
27+
28+
set -e
29+
30+
HOST="${HOST:-host.docker.internal}"
31+
PORT_BASE="${PORT_BASE:-8001}"
32+
EXAMPLES_INPUT="${EXAMPLES:-all}"
33+
IMAGE="${IMAGE:-examples}"
34+
CONTAINER_NAME="agentstack-examples"
35+
36+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
37+
38+
# Repository root (Dockerfile must be built from here)
39+
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
40+
41+
# Build Docker image
42+
do_build() {
43+
echo "Building Docker image: $IMAGE"
44+
docker build -t "$IMAGE" -f "$SCRIPT_DIR/Dockerfile" "$REPO_ROOT"
45+
}
46+
47+
# Discover available examples (alphabetical order)
48+
discover_examples() {
49+
AVAILABLE_EXAMPLES=()
50+
for dir in "$SCRIPT_DIR"/*/; do
51+
if [ -f "${dir}pyproject.toml" ]; then
52+
AVAILABLE_EXAMPLES+=("$(basename "$dir")")
53+
fi
54+
done
55+
}
56+
57+
# Build example list based on EXAMPLES input
58+
build_example_list() {
59+
discover_examples
60+
if [ "$EXAMPLES_INPUT" = "all" ]; then
61+
EXAMPLE_LIST=("${AVAILABLE_EXAMPLES[@]}")
62+
else
63+
IFS=',' read -ra EXAMPLE_LIST <<<"$EXAMPLES_INPUT"
64+
fi
65+
}
66+
67+
# Run Docker container
68+
do_run() {
69+
local detached="${1:-false}"
70+
build_example_list
71+
72+
NUM_EXAMPLES=${#EXAMPLE_LIST[@]}
73+
PORT_END=$((PORT_BASE + NUM_EXAMPLES - 1))
74+
EXAMPLES_STR=$(IFS=','; echo "${EXAMPLE_LIST[*]}")
75+
76+
echo "Starting examples: ${EXAMPLES_STR}"
77+
echo "Ports: ${PORT_BASE}-${PORT_END}"
78+
79+
local flags=""
80+
if [ "$detached" = "true" ]; then
81+
flags="-d"
82+
fi
83+
84+
docker run $flags \
85+
--name "$CONTAINER_NAME" \
86+
--rm \
87+
-e EXAMPLES="$EXAMPLES_STR" \
88+
-e PORT_BASE="$PORT_BASE" \
89+
-e PLATFORM_URL="http://host.docker.internal:8333" \
90+
-p "${PORT_BASE}-${PORT_END}:${PORT_BASE}-${PORT_END}" \
91+
"$IMAGE"
92+
}
93+
94+
# Register agents with agentstack
95+
do_add() {
96+
build_example_list
97+
local port=$PORT_BASE
98+
99+
for example_name in "${EXAMPLE_LIST[@]}"; do
100+
example_dir="$SCRIPT_DIR/$example_name"
101+
102+
if [ ! -d "$example_dir" ]; then
103+
echo "Warning: Example '$example_name' not found, skipping"
104+
continue
105+
fi
106+
107+
# Convert hyphenated name to underscored module name
108+
module_name="${example_name//-/_}"
109+
agent_file="${example_dir}/src/${module_name}/agent.py"
110+
111+
if [ ! -f "$agent_file" ]; then
112+
echo "Warning: agent.py not found for $example_name, skipping"
113+
continue
114+
fi
115+
116+
# Extract the agent function name (function decorated with @server.agent())
117+
agent_func=$(grep -A1 '@server.agent()' "$agent_file" | grep 'async def\|def ' | head -1 | sed 's/.*def \([a-zA-Z_][a-zA-Z0-9_]*\).*/\1/')
118+
119+
if [ -z "$agent_func" ]; then
120+
echo "Warning: Could not find agent function in $example_name, skipping"
121+
continue
122+
fi
123+
124+
url="http://${HOST}:${port}\#${agent_func}"
125+
echo "Adding $example_name: $url"
126+
agentstack add "$url"
127+
128+
((port++))
129+
done
130+
}
131+
132+
# Remove agents from agentstack
133+
do_remove() {
134+
build_example_list
135+
136+
for example_name in "${EXAMPLE_LIST[@]}"; do
137+
example_dir="$SCRIPT_DIR/$example_name"
138+
139+
if [ ! -d "$example_dir" ]; then
140+
echo "Warning: Example '$example_name' not found, skipping"
141+
continue
142+
fi
143+
144+
# Convert hyphenated name to underscored module name
145+
module_name="${example_name//-/_}"
146+
agent_file="${example_dir}/src/${module_name}/agent.py"
147+
148+
if [ ! -f "$agent_file" ]; then
149+
echo "Warning: agent.py not found for $example_name, skipping"
150+
continue
151+
fi
152+
153+
# Extract the agent function name (function decorated with @server.agent())
154+
agent_func=$(grep -A1 '@server.agent()' "$agent_file" | grep 'async def\|def ' | head -1 | sed 's/.*def \([a-zA-Z_][a-zA-Z0-9_]*\).*/\1/')
155+
156+
if [ -z "$agent_func" ]; then
157+
echo "Warning: Could not find agent function in $example_name, skipping"
158+
continue
159+
fi
160+
161+
echo "Removing $example_name: $agent_func"
162+
agentstack remove "$agent_func"
163+
done
164+
}
165+
166+
# Stop Docker container
167+
do_stop() {
168+
echo "Stopping $CONTAINER_NAME..."
169+
docker stop "$CONTAINER_NAME" 2>/dev/null || echo "Container not running"
170+
}
171+
172+
# Wait for container to be ready
173+
wait_for_ready() {
174+
build_example_list
175+
local port=$PORT_BASE
176+
local max_attempts=30
177+
178+
echo "Waiting for examples to be ready..."
179+
for example_name in "${EXAMPLE_LIST[@]}"; do
180+
for ((i=1; i<=max_attempts; i++)); do
181+
if curl -s "http://localhost:${port}/" >/dev/null 2>&1; then
182+
echo " $example_name (port $port): ready"
183+
break
184+
fi
185+
if [ $i -eq $max_attempts ]; then
186+
echo " $example_name (port $port): timeout waiting for ready"
187+
fi
188+
sleep 1
189+
done
190+
((port++))
191+
done
192+
}
193+
194+
# Main
195+
case "${1:-}" in
196+
build)
197+
do_build
198+
;;
199+
run)
200+
do_run false
201+
;;
202+
add)
203+
do_add
204+
;;
205+
remove)
206+
do_remove
207+
;;
208+
start)
209+
do_run true
210+
wait_for_ready
211+
do_add
212+
;;
213+
stop)
214+
do_stop
215+
;;
216+
*)
217+
echo "Usage: $0 {build|run|add|remove|start|stop}"
218+
echo ""
219+
echo "Commands:"
220+
echo " build - Build Docker image"
221+
echo " run - Run Docker container (foreground)"
222+
echo " add - Register agents with agentstack"
223+
echo " remove - Remove agents from agentstack"
224+
echo " start - Run Docker (background) and register agents"
225+
echo " stop - Stop the Docker container"
226+
exit 1
227+
;;
228+
esac

0 commit comments

Comments
 (0)