simple_acp 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.envrc +1 -0
- data/CHANGELOG.md +5 -0
- data/COMMITS.md +196 -0
- data/LICENSE.txt +21 -0
- data/README.md +385 -0
- data/Rakefile +13 -0
- data/docs/api/client-base.md +383 -0
- data/docs/api/index.md +159 -0
- data/docs/api/models.md +286 -0
- data/docs/api/server-base.md +379 -0
- data/docs/api/storage.md +347 -0
- data/docs/assets/images/simple_acp.jpg +0 -0
- data/docs/client/index.md +279 -0
- data/docs/client/sessions.md +324 -0
- data/docs/client/streaming.md +345 -0
- data/docs/client/sync-async.md +308 -0
- data/docs/core-concepts/agents.md +253 -0
- data/docs/core-concepts/events.md +337 -0
- data/docs/core-concepts/index.md +147 -0
- data/docs/core-concepts/messages.md +211 -0
- data/docs/core-concepts/runs.md +278 -0
- data/docs/core-concepts/sessions.md +281 -0
- data/docs/examples.md +659 -0
- data/docs/getting-started/configuration.md +166 -0
- data/docs/getting-started/index.md +62 -0
- data/docs/getting-started/installation.md +95 -0
- data/docs/getting-started/quick-start.md +189 -0
- data/docs/index.md +119 -0
- data/docs/server/creating-agents.md +360 -0
- data/docs/server/http-endpoints.md +411 -0
- data/docs/server/index.md +218 -0
- data/docs/server/multi-turn.md +329 -0
- data/docs/server/streaming.md +315 -0
- data/docs/storage/custom.md +414 -0
- data/docs/storage/index.md +176 -0
- data/docs/storage/memory.md +198 -0
- data/docs/storage/postgresql.md +350 -0
- data/docs/storage/redis.md +287 -0
- data/examples/01_basic/client.rb +88 -0
- data/examples/01_basic/server.rb +100 -0
- data/examples/02_async_execution/client.rb +107 -0
- data/examples/02_async_execution/server.rb +56 -0
- data/examples/03_run_management/client.rb +115 -0
- data/examples/03_run_management/server.rb +84 -0
- data/examples/04_rich_messages/client.rb +160 -0
- data/examples/04_rich_messages/server.rb +180 -0
- data/examples/05_await_resume/client.rb +164 -0
- data/examples/05_await_resume/server.rb +114 -0
- data/examples/06_agent_metadata/client.rb +188 -0
- data/examples/06_agent_metadata/server.rb +192 -0
- data/examples/README.md +252 -0
- data/examples/run_demo.sh +137 -0
- data/lib/simple_acp/client/base.rb +448 -0
- data/lib/simple_acp/client/sse.rb +141 -0
- data/lib/simple_acp/models/agent_manifest.rb +129 -0
- data/lib/simple_acp/models/await.rb +123 -0
- data/lib/simple_acp/models/base.rb +147 -0
- data/lib/simple_acp/models/errors.rb +102 -0
- data/lib/simple_acp/models/events.rb +256 -0
- data/lib/simple_acp/models/message.rb +235 -0
- data/lib/simple_acp/models/message_part.rb +225 -0
- data/lib/simple_acp/models/metadata.rb +161 -0
- data/lib/simple_acp/models/run.rb +298 -0
- data/lib/simple_acp/models/session.rb +137 -0
- data/lib/simple_acp/models/types.rb +210 -0
- data/lib/simple_acp/server/agent.rb +116 -0
- data/lib/simple_acp/server/app.rb +264 -0
- data/lib/simple_acp/server/base.rb +510 -0
- data/lib/simple_acp/server/context.rb +210 -0
- data/lib/simple_acp/server/falcon_runner.rb +61 -0
- data/lib/simple_acp/storage/base.rb +129 -0
- data/lib/simple_acp/storage/memory.rb +108 -0
- data/lib/simple_acp/storage/postgresql.rb +233 -0
- data/lib/simple_acp/storage/redis.rb +178 -0
- data/lib/simple_acp/version.rb +5 -0
- data/lib/simple_acp.rb +91 -0
- data/mkdocs.yml +152 -0
- data/sig/simple_acp.rbs +4 -0
- metadata +418 -0
data/docs/examples.md
ADDED
|
@@ -0,0 +1,659 @@
|
|
|
1
|
+
# Examples
|
|
2
|
+
|
|
3
|
+
!!! warning "CAUTION"
|
|
4
|
+
This project is under active development. The API and documentation may not necessarily reflect the current codebase.
|
|
5
|
+
|
|
6
|
+
This section provides detailed documentation for the example programs demonstrating SimpleAcp features. Each example is organized in its own subdirectory with both `server.rb` and `client.rb` files.
|
|
7
|
+
|
|
8
|
+
## Quick Start
|
|
9
|
+
|
|
10
|
+
Use the unified `run_demo.sh` script to run any demo:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# From the project root
|
|
14
|
+
./examples/run_demo.sh 1 # Run demo 1 (01_basic)
|
|
15
|
+
./examples/run_demo.sh 4 # Run demo 4 (04_rich_messages)
|
|
16
|
+
|
|
17
|
+
# Or from the examples directory
|
|
18
|
+
cd examples
|
|
19
|
+
./run_demo.sh 1
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Available Commands
|
|
23
|
+
|
|
24
|
+
| Command | Description |
|
|
25
|
+
|---------|-------------|
|
|
26
|
+
| `./run_demo.sh <1-6>` | Run a specific demo by number |
|
|
27
|
+
| `./run_demo.sh --list` | List all available demos |
|
|
28
|
+
| `./run_demo.sh --all` | Run all demos sequentially |
|
|
29
|
+
| `./run_demo.sh --help` | Show usage information |
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 01_basic - Core Functionality
|
|
34
|
+
|
|
35
|
+
**Location:** [`examples/01_basic/`](https://github.com/MadBomber/simple_acp/tree/main/examples/01_basic)
|
|
36
|
+
|
|
37
|
+
This example demonstrates the fundamental features of SimpleAcp including agent registration, synchronous execution, streaming responses, and session-based state management.
|
|
38
|
+
|
|
39
|
+
### Server Agents
|
|
40
|
+
|
|
41
|
+
| Agent | Description | Key Features |
|
|
42
|
+
|-------|-------------|--------------|
|
|
43
|
+
| `echo` | Echoes input messages back | Streaming with Enumerator, multiple message parts |
|
|
44
|
+
| `greeter` | Greets the user by name | Simple single-response pattern |
|
|
45
|
+
| `counter` | Counts invocations | Session state persistence with `context.state` and `context.set_state` |
|
|
46
|
+
| `gettysburg` | Recites the Gettysburg Address word by word | Streaming with timed delays, fiber-aware sleep |
|
|
47
|
+
| `assistant` | Remembers conversation history | Multi-turn conversations, `context.history` access |
|
|
48
|
+
|
|
49
|
+
### Demonstrated Capabilities
|
|
50
|
+
|
|
51
|
+
**Agent Registration:**
|
|
52
|
+
```ruby
|
|
53
|
+
server = SimpleAcp::Server::Base.new
|
|
54
|
+
|
|
55
|
+
server.agent("greeter", description: "Greets the user by name") do |context|
|
|
56
|
+
name = context.input.first&.text_content&.strip
|
|
57
|
+
name = "World" if name.nil? || name.empty?
|
|
58
|
+
SimpleAcp::Models::Message.agent("Hello, #{name}! Welcome to SimpleAcp.")
|
|
59
|
+
end
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Streaming Responses with Enumerator:**
|
|
63
|
+
```ruby
|
|
64
|
+
server.agent("echo", description: "Echoes everything you send") do |context|
|
|
65
|
+
Enumerator.new do |yielder|
|
|
66
|
+
context.input.each do |message|
|
|
67
|
+
yielder << SimpleAcp::Server::RunYield.new(
|
|
68
|
+
SimpleAcp::Models::Message.agent(message.text_content)
|
|
69
|
+
)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Session State Management:**
|
|
76
|
+
```ruby
|
|
77
|
+
server.agent("counter", description: "Counts how many times you've called it") do |context|
|
|
78
|
+
count = (context.state || 0) + 1
|
|
79
|
+
context.set_state(count)
|
|
80
|
+
SimpleAcp::Models::Message.agent("You have called me #{count} time(s).")
|
|
81
|
+
end
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Client Operations
|
|
85
|
+
|
|
86
|
+
- Health check with `client.ping`
|
|
87
|
+
- Listing available agents with `client.agents`
|
|
88
|
+
- Synchronous runs with `client.run_sync`
|
|
89
|
+
- Streaming responses with `client.run_stream`
|
|
90
|
+
- Session management with `client.use_session` and `client.clear_session`
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
client = SimpleAcp::Client::Base.new(base_url: "http://localhost:8000")
|
|
94
|
+
|
|
95
|
+
# Synchronous execution
|
|
96
|
+
run = client.run_sync(agent: "greeter", input: "Ruby Developer")
|
|
97
|
+
puts run.output.first.text_content
|
|
98
|
+
# => "Hello, Ruby Developer! Welcome to SimpleAcp."
|
|
99
|
+
|
|
100
|
+
# Streaming execution
|
|
101
|
+
client.run_stream(agent: "gettysburg", input: "recite") do |event|
|
|
102
|
+
case event
|
|
103
|
+
when SimpleAcp::Models::MessagePartEvent
|
|
104
|
+
print event.part.content
|
|
105
|
+
when SimpleAcp::Models::RunCompletedEvent
|
|
106
|
+
puts "\n[Streaming complete]"
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 02_async_execution - Non-Blocking Execution
|
|
114
|
+
|
|
115
|
+
**Location:** [`examples/02_async_execution/`](https://github.com/MadBomber/simple_acp/tree/main/examples/02_async_execution)
|
|
116
|
+
|
|
117
|
+
This example demonstrates asynchronous (non-blocking) execution patterns for handling long-running tasks without blocking the client.
|
|
118
|
+
|
|
119
|
+
### Server Agents
|
|
120
|
+
|
|
121
|
+
| Agent | Description | Key Features |
|
|
122
|
+
|-------|-------------|--------------|
|
|
123
|
+
| `slow-worker` | Simulates a 3-second task | Progress updates, streaming with timed iterations |
|
|
124
|
+
| `quick-status` | Returns status immediately | Instant response for comparison |
|
|
125
|
+
|
|
126
|
+
### Demonstrated Capabilities
|
|
127
|
+
|
|
128
|
+
**Async Execution with Manual Polling:**
|
|
129
|
+
```ruby
|
|
130
|
+
# Start task asynchronously
|
|
131
|
+
run = client.run_async(agent: "slow-worker", input: "data analysis")
|
|
132
|
+
puts "Run started with ID: #{run.run_id}"
|
|
133
|
+
|
|
134
|
+
# Poll for status
|
|
135
|
+
loop do
|
|
136
|
+
status = client.run_status(run.run_id)
|
|
137
|
+
puts "Status: #{status.status}"
|
|
138
|
+
break if status.terminal?
|
|
139
|
+
sleep(0.5)
|
|
140
|
+
end
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Simplified Waiting with `wait_for_run`:**
|
|
144
|
+
```ruby
|
|
145
|
+
run = client.run_async(agent: "slow-worker", input: "report generation")
|
|
146
|
+
completed_run = client.wait_for_run(run.run_id, timeout: 10)
|
|
147
|
+
|
|
148
|
+
if completed_run.status == "completed"
|
|
149
|
+
puts "Success!"
|
|
150
|
+
completed_run.output.each { |msg| puts msg.text_content }
|
|
151
|
+
end
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Concurrent Task Execution:**
|
|
155
|
+
```ruby
|
|
156
|
+
# Start multiple tasks concurrently
|
|
157
|
+
runs = %w[task_alpha task_beta task_gamma].map do |task_name|
|
|
158
|
+
client.run_async(agent: "slow-worker", input: task_name)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Wait for all to complete
|
|
162
|
+
runs.each do |run|
|
|
163
|
+
completed = client.wait_for_run(run.run_id, timeout: 15)
|
|
164
|
+
puts "Completed: #{completed.output.last&.text_content}"
|
|
165
|
+
end
|
|
166
|
+
# All 3 tasks complete in ~3s (concurrent), not 9s (sequential)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Key API Methods
|
|
170
|
+
|
|
171
|
+
| Method | Description |
|
|
172
|
+
|--------|-------------|
|
|
173
|
+
| `run_async` | Starts a run without blocking, returns immediately with run_id |
|
|
174
|
+
| `run_status` | Retrieves current status of a run by ID |
|
|
175
|
+
| `wait_for_run` | Blocks until run completes or timeout expires |
|
|
176
|
+
| `terminal?` | Checks if run status is terminal (completed, failed, cancelled) |
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## 03_run_management - Lifecycle Control
|
|
181
|
+
|
|
182
|
+
**Location:** [`examples/03_run_management/`](https://github.com/MadBomber/simple_acp/tree/main/examples/03_run_management)
|
|
183
|
+
|
|
184
|
+
This example demonstrates run lifecycle management including cancellation and event history retrieval.
|
|
185
|
+
|
|
186
|
+
### Server Agents
|
|
187
|
+
|
|
188
|
+
| Agent | Description | Key Features |
|
|
189
|
+
|-------|-------------|--------------|
|
|
190
|
+
| `cancellable-task` | A long-running task (10 iterations) | Cancellation checking with `context.cancelled?` |
|
|
191
|
+
| `event-generator` | Generates multiple events | Event history tracking, configurable event count |
|
|
192
|
+
|
|
193
|
+
### Demonstrated Capabilities
|
|
194
|
+
|
|
195
|
+
**Run Cancellation:**
|
|
196
|
+
```ruby
|
|
197
|
+
# Server-side: Check for cancellation
|
|
198
|
+
server.agent("cancellable-task", description: "A cancellable task") do |context|
|
|
199
|
+
Enumerator.new do |yielder|
|
|
200
|
+
10.times do |i|
|
|
201
|
+
if context.cancelled?
|
|
202
|
+
yielder << SimpleAcp::Server::RunYield.new(
|
|
203
|
+
SimpleAcp::Models::Message.agent("Task cancelled at iteration #{i + 1}")
|
|
204
|
+
)
|
|
205
|
+
break
|
|
206
|
+
end
|
|
207
|
+
# ... perform work ...
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Client-side: Cancel a running task
|
|
213
|
+
run = client.run_async(agent: "cancellable-task", input: "data processing")
|
|
214
|
+
sleep(1.5) # Let it run for a bit
|
|
215
|
+
cancelled_run = client.run_cancel(run.run_id)
|
|
216
|
+
puts "Status: #{cancelled_run.status}" # => "cancelled"
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Event History Retrieval:**
|
|
220
|
+
```ruby
|
|
221
|
+
run = client.run_sync(agent: "event-generator", input: "8")
|
|
222
|
+
|
|
223
|
+
# Get all events
|
|
224
|
+
events = client.run_events(run.run_id)
|
|
225
|
+
puts "Total events: #{events.length}"
|
|
226
|
+
|
|
227
|
+
# Group by event type
|
|
228
|
+
event_counts = events.group_by { |e| e.class.name.split("::").last }
|
|
229
|
+
event_counts.each { |type, evts| puts "#{type}: #{evts.length}" }
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
**Event Pagination:**
|
|
233
|
+
```ruby
|
|
234
|
+
# Page through events
|
|
235
|
+
page1 = client.run_events(run.run_id, limit: 5, offset: 0)
|
|
236
|
+
page2 = client.run_events(run.run_id, limit: 5, offset: 5)
|
|
237
|
+
page3 = client.run_events(run.run_id, limit: 5, offset: 10)
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Run States
|
|
241
|
+
|
|
242
|
+
| Status | Description |
|
|
243
|
+
|--------|-------------|
|
|
244
|
+
| `created` | Run has been created but not started |
|
|
245
|
+
| `in_progress` | Run is currently executing |
|
|
246
|
+
| `completed` | Run finished successfully |
|
|
247
|
+
| `failed` | Run encountered an error |
|
|
248
|
+
| `cancelled` | Run was cancelled by client |
|
|
249
|
+
| `awaiting` | Run is paused waiting for client input |
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## 04_rich_messages - Content Types
|
|
254
|
+
|
|
255
|
+
**Location:** [`examples/04_rich_messages/`](https://github.com/MadBomber/simple_acp/tree/main/examples/04_rich_messages)
|
|
256
|
+
|
|
257
|
+
This example demonstrates different message part types and content negotiation for multimodal agent responses.
|
|
258
|
+
|
|
259
|
+
### Server Agents
|
|
260
|
+
|
|
261
|
+
| Agent | Description | Output Types |
|
|
262
|
+
|-------|-------------|--------------|
|
|
263
|
+
| `json-data` | Returns structured JSON data | `application/json`, `text/plain` |
|
|
264
|
+
| `image-generator` | Generates SVG images as base64 | `image/svg+xml`, `text/plain` |
|
|
265
|
+
| `link-provider` | Returns URL references with metadata | `text/uri-list`, `text/plain` |
|
|
266
|
+
| `multi-format` | Returns data in multiple formats | `application/json`, `text/plain`, `text/html` |
|
|
267
|
+
|
|
268
|
+
### Demonstrated Capabilities
|
|
269
|
+
|
|
270
|
+
**JSON Message Parts:**
|
|
271
|
+
```ruby
|
|
272
|
+
server.agent("json-data",
|
|
273
|
+
description: "Returns structured JSON data",
|
|
274
|
+
output_content_types: ["application/json", "text/plain"]
|
|
275
|
+
) do |context|
|
|
276
|
+
data = {
|
|
277
|
+
query: "users",
|
|
278
|
+
count: 3,
|
|
279
|
+
results: [
|
|
280
|
+
{ id: 1, name: "Alice", role: "admin" },
|
|
281
|
+
{ id: 2, name: "Bob", role: "user" }
|
|
282
|
+
]
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
SimpleAcp::Models::Message.new(
|
|
286
|
+
role: "agent",
|
|
287
|
+
parts: [
|
|
288
|
+
SimpleAcp::Models::MessagePart.json(data),
|
|
289
|
+
SimpleAcp::Models::MessagePart.text("Query returned #{data[:count]} results")
|
|
290
|
+
]
|
|
291
|
+
)
|
|
292
|
+
end
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
**Base64 Encoded Binary Data:**
|
|
296
|
+
```ruby
|
|
297
|
+
server.agent("image-generator",
|
|
298
|
+
description: "Generates a simple SVG image",
|
|
299
|
+
output_content_types: ["image/svg+xml", "text/plain"]
|
|
300
|
+
) do |context|
|
|
301
|
+
color = context.input.first&.text_content || "blue"
|
|
302
|
+
|
|
303
|
+
svg = <<~SVG
|
|
304
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
|
|
305
|
+
<rect width="100" height="100" fill="transparent"/>
|
|
306
|
+
<circle cx="50" cy="50" r="40" fill="#{color}" opacity="0.8"/>
|
|
307
|
+
</svg>
|
|
308
|
+
SVG
|
|
309
|
+
|
|
310
|
+
encoded = Base64.strict_encode64(svg)
|
|
311
|
+
|
|
312
|
+
SimpleAcp::Models::Message.new(
|
|
313
|
+
role: "agent",
|
|
314
|
+
parts: [
|
|
315
|
+
SimpleAcp::Models::MessagePart.new(
|
|
316
|
+
kind: "data",
|
|
317
|
+
content: encoded,
|
|
318
|
+
content_type: "image/svg+xml"
|
|
319
|
+
)
|
|
320
|
+
]
|
|
321
|
+
)
|
|
322
|
+
end
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**URL References with Metadata:**
|
|
326
|
+
```ruby
|
|
327
|
+
# Message part with metadata
|
|
328
|
+
SimpleAcp::Models::MessagePart.new(
|
|
329
|
+
kind: "text",
|
|
330
|
+
content: "Ruby Official Site: https://www.ruby-lang.org/",
|
|
331
|
+
content_type: "text/plain",
|
|
332
|
+
metadata: { url: "https://www.ruby-lang.org/", title: "Ruby Official Site" }
|
|
333
|
+
)
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
**Client-Side Content Type Handling:**
|
|
337
|
+
```ruby
|
|
338
|
+
run = client.run_sync(agent: "json-data", input: "users")
|
|
339
|
+
|
|
340
|
+
run.output.each do |message|
|
|
341
|
+
message.parts.each do |part|
|
|
342
|
+
case part.content_type
|
|
343
|
+
when "application/json"
|
|
344
|
+
data = JSON.parse(part.content)
|
|
345
|
+
puts "Query: #{data['query']}, Count: #{data['count']}"
|
|
346
|
+
when "text/plain"
|
|
347
|
+
puts part.content
|
|
348
|
+
when "image/svg+xml"
|
|
349
|
+
svg = Base64.decode64(part.content)
|
|
350
|
+
puts "SVG received: #{svg.length} bytes"
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Message Part Types
|
|
357
|
+
|
|
358
|
+
| Kind | Content Type | Usage |
|
|
359
|
+
|------|--------------|-------|
|
|
360
|
+
| `text` | `text/plain` | Plain text content |
|
|
361
|
+
| `text` | `text/html` | HTML formatted content |
|
|
362
|
+
| `text` | `text/markdown` | Markdown content |
|
|
363
|
+
| `data` | `application/json` | Structured JSON data |
|
|
364
|
+
| `data` | `image/*` | Base64 encoded images |
|
|
365
|
+
| `text` | `text/uri-list` | URL references |
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## 05_await_resume - Interactive Flows
|
|
370
|
+
|
|
371
|
+
**Location:** [`examples/05_await_resume/`](https://github.com/MadBomber/simple_acp/tree/main/examples/05_await_resume)
|
|
372
|
+
|
|
373
|
+
This example demonstrates the await/resume pattern for building interactive multi-step agent flows that pause execution to request input from the client.
|
|
374
|
+
|
|
375
|
+
### Server Agents
|
|
376
|
+
|
|
377
|
+
| Agent | Description | Key Features |
|
|
378
|
+
|-------|-------------|--------------|
|
|
379
|
+
| `greeter` | Asks for your name, then greets you | Single await/resume cycle |
|
|
380
|
+
| `survey` | A multi-step survey | Multiple awaits with state tracking |
|
|
381
|
+
| `confirmer` | Asks for confirmation before action | Yes/no confirmation pattern |
|
|
382
|
+
|
|
383
|
+
### Demonstrated Capabilities
|
|
384
|
+
|
|
385
|
+
**Simple Await/Resume:**
|
|
386
|
+
```ruby
|
|
387
|
+
server.agent("greeter", description: "Asks for your name and greets you") do |context|
|
|
388
|
+
if context.resume_message
|
|
389
|
+
# Resume: we have the client's response
|
|
390
|
+
name = context.resume_message.text_content
|
|
391
|
+
SimpleAcp::Models::Message.agent("Hello, #{name}! Nice to meet you!")
|
|
392
|
+
else
|
|
393
|
+
# Initial call: ask for input
|
|
394
|
+
context.await_message(
|
|
395
|
+
SimpleAcp::Models::Message.agent("What is your name?")
|
|
396
|
+
)
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
**Multi-Step Wizard with State:**
|
|
402
|
+
```ruby
|
|
403
|
+
server.agent("survey", description: "A multi-step survey") do |context|
|
|
404
|
+
survey_state = context.state || { step: 0, answers: {} }
|
|
405
|
+
step = survey_state[:step]
|
|
406
|
+
|
|
407
|
+
case step
|
|
408
|
+
when 0
|
|
409
|
+
context.set_state({ step: 1, answers: {} })
|
|
410
|
+
context.await_message(
|
|
411
|
+
SimpleAcp::Models::Message.agent("Question 1: What is your name?")
|
|
412
|
+
)
|
|
413
|
+
when 1
|
|
414
|
+
answers = survey_state[:answers].merge("name" => context.resume_message&.text_content)
|
|
415
|
+
context.set_state({ step: 2, answers: answers })
|
|
416
|
+
context.await_message(
|
|
417
|
+
SimpleAcp::Models::Message.agent("Question 2: What is your favorite color?")
|
|
418
|
+
)
|
|
419
|
+
when 2
|
|
420
|
+
# Final step: return summary
|
|
421
|
+
answers = survey_state[:answers].merge("color" => context.resume_message&.text_content)
|
|
422
|
+
SimpleAcp::Models::Message.agent("Survey complete! Answers: #{answers}")
|
|
423
|
+
end
|
|
424
|
+
end
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
**Confirmation Pattern:**
|
|
428
|
+
```ruby
|
|
429
|
+
server.agent("confirmer", description: "Performs action after confirmation") do |context|
|
|
430
|
+
action = context.input.first&.text_content
|
|
431
|
+
state = context.state || { confirmed: nil }
|
|
432
|
+
|
|
433
|
+
if state[:confirmed].nil?
|
|
434
|
+
context.set_state({ action: action, confirmed: false })
|
|
435
|
+
context.await_message(
|
|
436
|
+
SimpleAcp::Models::Message.agent("Are you sure you want to '#{action}'? (yes/no)")
|
|
437
|
+
)
|
|
438
|
+
else
|
|
439
|
+
response = context.resume_message&.text_content&.downcase&.strip
|
|
440
|
+
if %w[yes y sure ok].include?(response)
|
|
441
|
+
SimpleAcp::Models::Message.agent("Action '#{state[:action]}' confirmed and executed!")
|
|
442
|
+
else
|
|
443
|
+
SimpleAcp::Models::Message.agent("Action '#{state[:action]}' cancelled.")
|
|
444
|
+
end
|
|
445
|
+
end
|
|
446
|
+
end
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
**Client-Side Await Handling:**
|
|
450
|
+
```ruby
|
|
451
|
+
# Detect awaiting status and resume
|
|
452
|
+
run = client.run_sync(agent: "greeter", input: "start")
|
|
453
|
+
|
|
454
|
+
if run.status == "awaiting"
|
|
455
|
+
puts "Agent asks: #{run.await_request&.message&.text_content}"
|
|
456
|
+
|
|
457
|
+
resume = SimpleAcp::Models::MessageAwaitResume.new(
|
|
458
|
+
message: SimpleAcp::Models::Message.user("Alice")
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
completed_run = client.run_resume_sync(run_id: run.run_id, await_resume: resume)
|
|
462
|
+
puts "Agent says: #{completed_run.output.last&.text_content}"
|
|
463
|
+
end
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
**Streaming Resume:**
|
|
467
|
+
```ruby
|
|
468
|
+
client.run_resume_stream(run_id: run.run_id, await_resume: resume) do |event|
|
|
469
|
+
case event
|
|
470
|
+
when SimpleAcp::Models::RunInProgressEvent
|
|
471
|
+
print "[in_progress] "
|
|
472
|
+
when SimpleAcp::Models::RunCompletedEvent
|
|
473
|
+
puts "Final: #{event.run.output.last&.text_content}"
|
|
474
|
+
end
|
|
475
|
+
end
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### Key API Methods
|
|
479
|
+
|
|
480
|
+
| Method | Description |
|
|
481
|
+
|--------|-------------|
|
|
482
|
+
| `context.await_message` | Pauses execution and requests client input |
|
|
483
|
+
| `context.resume_message` | Access the client's response when resuming |
|
|
484
|
+
| `run.await_request` | Get the await request details from a paused run |
|
|
485
|
+
| `run_resume_sync` | Resume a paused run synchronously |
|
|
486
|
+
| `run_resume_stream` | Resume a paused run with streaming response |
|
|
487
|
+
|
|
488
|
+
---
|
|
489
|
+
|
|
490
|
+
## 06_agent_metadata - Rich Metadata
|
|
491
|
+
|
|
492
|
+
**Location:** [`examples/06_agent_metadata/`](https://github.com/MadBomber/simple_acp/tree/main/examples/06_agent_metadata)
|
|
493
|
+
|
|
494
|
+
This example demonstrates rich agent metadata including author information, capabilities, documentation, and content type negotiation.
|
|
495
|
+
|
|
496
|
+
### Server Agents
|
|
497
|
+
|
|
498
|
+
| Agent | Description | Metadata Level |
|
|
499
|
+
|-------|-------------|----------------|
|
|
500
|
+
| `text-analyzer` | Analyzes text for sentiment, keywords, summaries | Full metadata (author, capabilities, links, dependencies) |
|
|
501
|
+
| `simple-echo` | A simple echo agent | Minimal metadata |
|
|
502
|
+
| `json-processor` | Processes JSON input/output | Partial metadata (domains, tags) |
|
|
503
|
+
|
|
504
|
+
### Demonstrated Capabilities
|
|
505
|
+
|
|
506
|
+
**Full Agent Metadata:**
|
|
507
|
+
```ruby
|
|
508
|
+
metadata = SimpleAcp::Models::Metadata.new(
|
|
509
|
+
documentation: "A comprehensive text analysis agent...",
|
|
510
|
+
license: "MIT",
|
|
511
|
+
programming_language: "ruby",
|
|
512
|
+
natural_languages: ["en", "es", "fr"],
|
|
513
|
+
framework: "SimpleAcp",
|
|
514
|
+
created_at: "2024-01-15T10:00:00Z",
|
|
515
|
+
updated_at: "2024-06-20T14:30:00Z",
|
|
516
|
+
|
|
517
|
+
author: SimpleAcp::Models::Author.new(
|
|
518
|
+
name: "Alice Developer",
|
|
519
|
+
email: "alice@example.com",
|
|
520
|
+
url: "https://github.com/alicedev"
|
|
521
|
+
),
|
|
522
|
+
|
|
523
|
+
contributors: [
|
|
524
|
+
SimpleAcp::Models::Contributor.new(name: "Bob Contributor", email: "bob@example.com"),
|
|
525
|
+
SimpleAcp::Models::Contributor.new(name: "Charlie Helper", url: "https://github.com/charliehelper")
|
|
526
|
+
],
|
|
527
|
+
|
|
528
|
+
capabilities: [
|
|
529
|
+
SimpleAcp::Models::Capability.new(name: "summarize", description: "Summarize long text"),
|
|
530
|
+
SimpleAcp::Models::Capability.new(name: "sentiment", description: "Analyze sentiment"),
|
|
531
|
+
SimpleAcp::Models::Capability.new(name: "keywords", description: "Extract keywords")
|
|
532
|
+
],
|
|
533
|
+
|
|
534
|
+
domains: ["nlp", "text-analysis", "ai"],
|
|
535
|
+
tags: ["text", "analysis", "nlp", "summarization"],
|
|
536
|
+
|
|
537
|
+
links: [
|
|
538
|
+
SimpleAcp::Models::Link.new(type: "documentation", url: "https://example.com/docs"),
|
|
539
|
+
SimpleAcp::Models::Link.new(type: "source-code", url: "https://github.com/example/repo"),
|
|
540
|
+
SimpleAcp::Models::Link.new(type: "homepage", url: "https://example.com")
|
|
541
|
+
],
|
|
542
|
+
|
|
543
|
+
dependencies: [
|
|
544
|
+
SimpleAcp::Models::Dependency.new(type: "agent", name: "tokenizer"),
|
|
545
|
+
SimpleAcp::Models::Dependency.new(type: "model", name: "gpt-4")
|
|
546
|
+
],
|
|
547
|
+
|
|
548
|
+
recommended_models: ["gpt-4", "claude-3", "llama-2"]
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
server.agent("text-analyzer",
|
|
552
|
+
description: "Analyzes text for sentiment, keywords, and provides summaries",
|
|
553
|
+
input_content_types: ["text/plain", "text/markdown", "application/json"],
|
|
554
|
+
output_content_types: ["application/json", "text/plain"],
|
|
555
|
+
metadata: metadata
|
|
556
|
+
) do |context|
|
|
557
|
+
# Agent implementation...
|
|
558
|
+
end
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
**Client-Side Metadata Inspection:**
|
|
562
|
+
```ruby
|
|
563
|
+
agent = client.agent("text-analyzer")
|
|
564
|
+
|
|
565
|
+
puts "Agent: #{agent.name}"
|
|
566
|
+
puts "Description: #{agent.description}"
|
|
567
|
+
|
|
568
|
+
if agent.metadata
|
|
569
|
+
meta = agent.metadata
|
|
570
|
+
|
|
571
|
+
puts "Author: #{meta.author.name} (#{meta.author.email})"
|
|
572
|
+
|
|
573
|
+
meta.capabilities.each do |cap|
|
|
574
|
+
puts "Capability: #{cap.name} - #{cap.description}"
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
meta.links.each do |link|
|
|
578
|
+
puts "Link: #{link.type} -> #{link.url}"
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
puts "Domains: #{meta.domains.join(', ')}"
|
|
582
|
+
puts "Tags: #{meta.tags.join(', ')}"
|
|
583
|
+
end
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
**Content Type Negotiation:**
|
|
587
|
+
```ruby
|
|
588
|
+
agent = client.agent("text-analyzer")
|
|
589
|
+
|
|
590
|
+
# Check if agent accepts specific content types
|
|
591
|
+
agent.accepts_content_type?("text/plain") # => true
|
|
592
|
+
agent.accepts_content_type?("text/markdown") # => true
|
|
593
|
+
agent.accepts_content_type?("image/png") # => false
|
|
594
|
+
|
|
595
|
+
# Check if agent produces specific content types
|
|
596
|
+
agent.produces_content_type?("application/json") # => true
|
|
597
|
+
agent.produces_content_type?("text/html") # => false
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
### Metadata Fields
|
|
601
|
+
|
|
602
|
+
| Field | Type | Description |
|
|
603
|
+
|-------|------|-------------|
|
|
604
|
+
| `documentation` | String | Detailed documentation text |
|
|
605
|
+
| `license` | String | License identifier (e.g., "MIT") |
|
|
606
|
+
| `programming_language` | String | Implementation language |
|
|
607
|
+
| `natural_languages` | Array | Supported human languages |
|
|
608
|
+
| `framework` | String | Framework used |
|
|
609
|
+
| `author` | Author | Primary author information |
|
|
610
|
+
| `contributors` | Array | List of contributors |
|
|
611
|
+
| `capabilities` | Array | Agent capabilities with descriptions |
|
|
612
|
+
| `domains` | Array | Domain categories |
|
|
613
|
+
| `tags` | Array | Searchable tags |
|
|
614
|
+
| `links` | Array | Related URLs |
|
|
615
|
+
| `dependencies` | Array | Required dependencies |
|
|
616
|
+
| `recommended_models` | Array | Suggested AI models |
|
|
617
|
+
| `created_at` | String | Creation timestamp |
|
|
618
|
+
| `updated_at` | String | Last update timestamp |
|
|
619
|
+
|
|
620
|
+
---
|
|
621
|
+
|
|
622
|
+
## Feature Coverage
|
|
623
|
+
|
|
624
|
+
| Feature | Example |
|
|
625
|
+
|---------|---------|
|
|
626
|
+
| Basic agent registration | 01_basic |
|
|
627
|
+
| Streaming responses | 01_basic |
|
|
628
|
+
| Session state | 01_basic |
|
|
629
|
+
| Conversation history | 01_basic |
|
|
630
|
+
| Async execution | 02_async_execution |
|
|
631
|
+
| Polling (`run_status`, `wait_for_run`) | 02_async_execution |
|
|
632
|
+
| Concurrent runs | 02_async_execution |
|
|
633
|
+
| Run cancellation | 03_run_management |
|
|
634
|
+
| Event history | 03_run_management |
|
|
635
|
+
| JSON message parts | 04_rich_messages |
|
|
636
|
+
| Binary/base64 data | 04_rich_messages |
|
|
637
|
+
| URL references | 04_rich_messages |
|
|
638
|
+
| Content type negotiation | 04_rich_messages, 06_agent_metadata |
|
|
639
|
+
| Await/resume pattern | 05_await_resume |
|
|
640
|
+
| Multi-step flows | 05_await_resume |
|
|
641
|
+
| Agent metadata | 06_agent_metadata |
|
|
642
|
+
| Author/contributor info | 06_agent_metadata |
|
|
643
|
+
| Capabilities and dependencies | 06_agent_metadata |
|
|
644
|
+
|
|
645
|
+
---
|
|
646
|
+
|
|
647
|
+
## Running Examples Manually
|
|
648
|
+
|
|
649
|
+
If you prefer to run server and client manually in separate terminals:
|
|
650
|
+
|
|
651
|
+
```bash
|
|
652
|
+
# Terminal 1: Start server
|
|
653
|
+
ruby examples/01_basic/server.rb
|
|
654
|
+
|
|
655
|
+
# Terminal 2: Run client
|
|
656
|
+
ruby examples/01_basic/client.rb
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
The `run_demo.sh` script handles starting the server, waiting for it to be ready, running the client, and cleanup automatically.
|