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
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
# Events
|
|
2
|
+
|
|
3
|
+
Events provide real-time updates during run execution. They enable streaming responses, progress tracking, and reactive client applications.
|
|
4
|
+
|
|
5
|
+
## Event Types
|
|
6
|
+
|
|
7
|
+
```mermaid
|
|
8
|
+
graph LR
|
|
9
|
+
R[Run] --> E1[RunStartedEvent]
|
|
10
|
+
R --> E2[MessageCreatedEvent]
|
|
11
|
+
R --> E3[MessagePartEvent]
|
|
12
|
+
R --> E4[RunCompletedEvent]
|
|
13
|
+
R --> E5[RunFailedEvent]
|
|
14
|
+
R --> E6[RunAwaitingEvent]
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### RunStartedEvent
|
|
18
|
+
|
|
19
|
+
Emitted when a run begins execution:
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
event.type # => "run_started"
|
|
23
|
+
event.run_id # => "550e8400-..."
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### MessageCreatedEvent
|
|
27
|
+
|
|
28
|
+
Emitted when a new message is created:
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
event.type # => "message_created"
|
|
32
|
+
event.message # => Message object
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### MessagePartEvent
|
|
36
|
+
|
|
37
|
+
Emitted for each part of a message (enables streaming):
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
event.type # => "message_part"
|
|
41
|
+
event.part # => MessagePart object
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### MessageCompletedEvent
|
|
45
|
+
|
|
46
|
+
Emitted when a message is fully assembled:
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
event.type # => "message_completed"
|
|
50
|
+
event.message # => Complete Message object
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### RunCompletedEvent
|
|
54
|
+
|
|
55
|
+
Emitted when a run finishes successfully:
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
event.type # => "run_completed"
|
|
59
|
+
event.run # => Completed Run object
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### RunFailedEvent
|
|
63
|
+
|
|
64
|
+
Emitted when a run encounters an error:
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
event.type # => "run_failed"
|
|
68
|
+
event.run # => Failed Run object
|
|
69
|
+
event.error # => Error object
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### RunAwaitingEvent
|
|
73
|
+
|
|
74
|
+
Emitted when a run needs additional input:
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
event.type # => "run_awaiting"
|
|
78
|
+
event.run # => Run object
|
|
79
|
+
event.await_request # => AwaitRequest object
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Consuming Events
|
|
83
|
+
|
|
84
|
+
### Streaming Client
|
|
85
|
+
|
|
86
|
+
```ruby
|
|
87
|
+
client.run_stream(agent: "chat", input: messages) do |event|
|
|
88
|
+
case event
|
|
89
|
+
when SimpleAcp::Models::RunStartedEvent
|
|
90
|
+
puts "Run #{event.run_id} started"
|
|
91
|
+
|
|
92
|
+
when SimpleAcp::Models::MessageCreatedEvent
|
|
93
|
+
puts "New message from #{event.message.role}"
|
|
94
|
+
|
|
95
|
+
when SimpleAcp::Models::MessagePartEvent
|
|
96
|
+
# Stream content as it arrives
|
|
97
|
+
print event.part.content
|
|
98
|
+
$stdout.flush
|
|
99
|
+
|
|
100
|
+
when SimpleAcp::Models::MessageCompletedEvent
|
|
101
|
+
puts "\n--- Message complete ---"
|
|
102
|
+
|
|
103
|
+
when SimpleAcp::Models::RunCompletedEvent
|
|
104
|
+
puts "\nRun completed!"
|
|
105
|
+
|
|
106
|
+
when SimpleAcp::Models::RunFailedEvent
|
|
107
|
+
puts "\nError: #{event.error.message}"
|
|
108
|
+
|
|
109
|
+
when SimpleAcp::Models::RunAwaitingEvent
|
|
110
|
+
puts "\nAwaiting input..."
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Fetching Historical Events
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
events = client.run_events(run_id)
|
|
119
|
+
|
|
120
|
+
events.each do |event|
|
|
121
|
+
puts "#{event.type}: #{event.inspect}"
|
|
122
|
+
end
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Server-Side Events
|
|
126
|
+
|
|
127
|
+
### Yielding Events
|
|
128
|
+
|
|
129
|
+
Agents emit events by yielding from an Enumerator:
|
|
130
|
+
|
|
131
|
+
```ruby
|
|
132
|
+
server.agent("streamer") do |context|
|
|
133
|
+
Enumerator.new do |yielder|
|
|
134
|
+
words = context.input.first&.text_content&.split || []
|
|
135
|
+
|
|
136
|
+
words.each do |word|
|
|
137
|
+
# Each yield creates events
|
|
138
|
+
yielder << SimpleAcp::Server::RunYield.new(
|
|
139
|
+
SimpleAcp::Models::Message.agent(word + " ")
|
|
140
|
+
)
|
|
141
|
+
sleep 0.1
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Event Flow
|
|
148
|
+
|
|
149
|
+
```mermaid
|
|
150
|
+
sequenceDiagram
|
|
151
|
+
participant Client
|
|
152
|
+
participant Server
|
|
153
|
+
participant Agent
|
|
154
|
+
|
|
155
|
+
Client->>Server: run_stream
|
|
156
|
+
Server->>Agent: execute
|
|
157
|
+
|
|
158
|
+
Agent-->>Server: yield message
|
|
159
|
+
Server-->>Client: RunStartedEvent
|
|
160
|
+
Server-->>Client: MessageCreatedEvent
|
|
161
|
+
Server-->>Client: MessagePartEvent
|
|
162
|
+
|
|
163
|
+
Agent-->>Server: yield message
|
|
164
|
+
Server-->>Client: MessageCreatedEvent
|
|
165
|
+
Server-->>Client: MessagePartEvent
|
|
166
|
+
|
|
167
|
+
Agent-->>Server: return
|
|
168
|
+
Server-->>Client: RunCompletedEvent
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Server-Sent Events (SSE)
|
|
172
|
+
|
|
173
|
+
When using HTTP streaming, events are delivered via Server-Sent Events:
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
GET /runs HTTP/1.1
|
|
177
|
+
Accept: text/event-stream
|
|
178
|
+
|
|
179
|
+
HTTP/1.1 200 OK
|
|
180
|
+
Content-Type: text/event-stream
|
|
181
|
+
|
|
182
|
+
event: run_started
|
|
183
|
+
data: {"run_id":"550e8400-..."}
|
|
184
|
+
|
|
185
|
+
event: message_part
|
|
186
|
+
data: {"part":{"content_type":"text/plain","content":"Hello"}}
|
|
187
|
+
|
|
188
|
+
event: run_completed
|
|
189
|
+
data: {"run":{...}}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Raw SSE Handling
|
|
193
|
+
|
|
194
|
+
```ruby
|
|
195
|
+
require 'faraday'
|
|
196
|
+
|
|
197
|
+
conn = Faraday.new(url: 'http://localhost:8000')
|
|
198
|
+
|
|
199
|
+
conn.post('/runs') do |req|
|
|
200
|
+
req.headers['Accept'] = 'text/event-stream'
|
|
201
|
+
req.body = { agent_name: 'chat', input: [...] }.to_json
|
|
202
|
+
|
|
203
|
+
req.options.on_data = proc do |chunk, _|
|
|
204
|
+
# Parse SSE format
|
|
205
|
+
chunk.split("\n\n").each do |event_text|
|
|
206
|
+
# Process event...
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Common Patterns
|
|
213
|
+
|
|
214
|
+
### Progress Indicator
|
|
215
|
+
|
|
216
|
+
```ruby
|
|
217
|
+
total = 0
|
|
218
|
+
client.run_stream(agent: "processor", input: data) do |event|
|
|
219
|
+
case event
|
|
220
|
+
when SimpleAcp::Models::MessagePartEvent
|
|
221
|
+
total += 1
|
|
222
|
+
print "\rProcessed: #{total} items"
|
|
223
|
+
when SimpleAcp::Models::RunCompletedEvent
|
|
224
|
+
puts "\nDone!"
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Collecting All Output
|
|
230
|
+
|
|
231
|
+
```ruby
|
|
232
|
+
messages = []
|
|
233
|
+
|
|
234
|
+
client.run_stream(agent: "chat", input: question) do |event|
|
|
235
|
+
case event
|
|
236
|
+
when SimpleAcp::Models::MessageCompletedEvent
|
|
237
|
+
messages << event.message
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
puts "Received #{messages.length} messages"
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Error Recovery
|
|
245
|
+
|
|
246
|
+
```ruby
|
|
247
|
+
begin
|
|
248
|
+
client.run_stream(agent: "risky", input: data) do |event|
|
|
249
|
+
case event
|
|
250
|
+
when SimpleAcp::Models::RunFailedEvent
|
|
251
|
+
log_error(event.error)
|
|
252
|
+
# Don't raise, handle gracefully
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
rescue Faraday::TimeoutError
|
|
256
|
+
puts "Stream timed out"
|
|
257
|
+
# Retry logic...
|
|
258
|
+
end
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Building Real-Time UI
|
|
262
|
+
|
|
263
|
+
```ruby
|
|
264
|
+
# In a web application
|
|
265
|
+
def stream_response(agent, input)
|
|
266
|
+
response.headers['Content-Type'] = 'text/event-stream'
|
|
267
|
+
|
|
268
|
+
client.run_stream(agent: agent, input: input) do |event|
|
|
269
|
+
case event
|
|
270
|
+
when SimpleAcp::Models::MessagePartEvent
|
|
271
|
+
response.write("data: #{event.part.content.to_json}\n\n")
|
|
272
|
+
when SimpleAcp::Models::RunCompletedEvent
|
|
273
|
+
response.write("event: done\ndata: {}\n\n")
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## Event Persistence
|
|
280
|
+
|
|
281
|
+
Events are stored and can be retrieved later:
|
|
282
|
+
|
|
283
|
+
```ruby
|
|
284
|
+
# Run completes
|
|
285
|
+
run = client.run_sync(agent: "logger", input: [...])
|
|
286
|
+
|
|
287
|
+
# Later, retrieve events
|
|
288
|
+
events = client.run_events(run.run_id)
|
|
289
|
+
events.each { |e| puts e.type }
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Best Practices
|
|
293
|
+
|
|
294
|
+
### Handle All Event Types
|
|
295
|
+
|
|
296
|
+
```ruby
|
|
297
|
+
client.run_stream(agent: "...", input: [...]) do |event|
|
|
298
|
+
case event
|
|
299
|
+
when SimpleAcp::Models::RunStartedEvent
|
|
300
|
+
# Handle start
|
|
301
|
+
when SimpleAcp::Models::MessagePartEvent
|
|
302
|
+
# Handle streaming content
|
|
303
|
+
when SimpleAcp::Models::RunCompletedEvent
|
|
304
|
+
# Handle completion
|
|
305
|
+
when SimpleAcp::Models::RunFailedEvent
|
|
306
|
+
# Handle errors
|
|
307
|
+
when SimpleAcp::Models::RunAwaitingEvent
|
|
308
|
+
# Handle await
|
|
309
|
+
else
|
|
310
|
+
# Log unknown events
|
|
311
|
+
logger.warn("Unknown event: #{event.type}")
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Use Events for UX
|
|
317
|
+
|
|
318
|
+
```ruby
|
|
319
|
+
# Show typing indicator
|
|
320
|
+
client.run_stream(agent: "chat", input: [...]) do |event|
|
|
321
|
+
case event
|
|
322
|
+
when SimpleAcp::Models::RunStartedEvent
|
|
323
|
+
show_typing_indicator
|
|
324
|
+
when SimpleAcp::Models::MessagePartEvent
|
|
325
|
+
hide_typing_indicator
|
|
326
|
+
append_content(event.part.content)
|
|
327
|
+
when SimpleAcp::Models::RunCompletedEvent
|
|
328
|
+
mark_complete
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## Next Steps
|
|
334
|
+
|
|
335
|
+
- Explore [Client Streaming](../client/streaming.md) for advanced patterns
|
|
336
|
+
- Learn about [Server Streaming](../server/streaming.md) for generating events
|
|
337
|
+
- See the [API Reference](../api/models.md) for event model details
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Core Concepts
|
|
2
|
+
|
|
3
|
+
Understanding the core concepts of the Agent Communication Protocol (ACP) is essential for building effective agent systems with SimpleAcp.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
ACP defines a standardized way for agents, applications, and humans to communicate. The protocol centers around these key concepts:
|
|
8
|
+
|
|
9
|
+
```mermaid
|
|
10
|
+
graph TB
|
|
11
|
+
subgraph "Core Concepts"
|
|
12
|
+
M[Messages] --> R[Runs]
|
|
13
|
+
A[Agents] --> R
|
|
14
|
+
R --> S[Sessions]
|
|
15
|
+
R --> E[Events]
|
|
16
|
+
end
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## The Building Blocks
|
|
20
|
+
|
|
21
|
+
### Messages
|
|
22
|
+
|
|
23
|
+
[Messages](messages.md) are the fundamental unit of communication. They carry content between users and agents.
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
# User sends a message
|
|
27
|
+
message = SimpleAcp::Models::Message.user("What's the weather?")
|
|
28
|
+
|
|
29
|
+
# Agent responds with a message
|
|
30
|
+
response = SimpleAcp::Models::Message.agent("It's sunny and 72°F")
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Agents
|
|
34
|
+
|
|
35
|
+
[Agents](agents.md) are the processing units that receive input and produce output. They're registered on servers and can be invoked by clients.
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
server.agent("weather", description: "Gets weather information") do |context|
|
|
39
|
+
# Process context.input and return response
|
|
40
|
+
end
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Runs
|
|
44
|
+
|
|
45
|
+
[Runs](runs.md) represent a single execution of an agent. They track the lifecycle from start to completion, including any errors or awaiting states.
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
run = client.run_sync(agent: "weather", input: [...])
|
|
49
|
+
puts run.status # "completed"
|
|
50
|
+
puts run.output # Response messages
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Sessions
|
|
54
|
+
|
|
55
|
+
[Sessions](sessions.md) maintain state across multiple runs, enabling multi-turn conversations and stateful interactions.
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
client.use_session("conversation-123")
|
|
59
|
+
# All subsequent runs share this session
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Events
|
|
63
|
+
|
|
64
|
+
[Events](events.md) provide real-time updates during run execution, enabling streaming responses and progress tracking.
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
client.run_stream(agent: "...", input: [...]) do |event|
|
|
68
|
+
case event
|
|
69
|
+
when SimpleAcp::Models::MessagePartEvent
|
|
70
|
+
print event.part.content # Stream as it arrives
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## How They Work Together
|
|
76
|
+
|
|
77
|
+
Here's how these concepts interact in a typical flow:
|
|
78
|
+
|
|
79
|
+
```mermaid
|
|
80
|
+
sequenceDiagram
|
|
81
|
+
participant Client
|
|
82
|
+
participant Server
|
|
83
|
+
participant Agent
|
|
84
|
+
participant Storage
|
|
85
|
+
|
|
86
|
+
Client->>Server: Create Run (Messages)
|
|
87
|
+
Server->>Storage: Store Run
|
|
88
|
+
Server->>Agent: Execute with Context
|
|
89
|
+
Agent-->>Server: Events (streaming)
|
|
90
|
+
Server-->>Client: Events (SSE)
|
|
91
|
+
Agent->>Server: Output Messages
|
|
92
|
+
Server->>Storage: Update Run
|
|
93
|
+
Server->>Client: Run Completed
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
1. **Client** creates a run by sending input messages to the server
|
|
97
|
+
2. **Server** creates a Run record and invokes the appropriate agent
|
|
98
|
+
3. **Agent** processes the input through its handler, optionally yielding events
|
|
99
|
+
4. **Events** stream back to the client in real-time
|
|
100
|
+
5. **Run** completes with output messages
|
|
101
|
+
6. **Session** (if used) maintains state for subsequent runs
|
|
102
|
+
|
|
103
|
+
## Deep Dives
|
|
104
|
+
|
|
105
|
+
<div class="grid cards" markdown>
|
|
106
|
+
|
|
107
|
+
- :material-message-text:{ .lg .middle } **Messages**
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
Learn about message structure, parts, and content types
|
|
112
|
+
|
|
113
|
+
[:octicons-arrow-right-24: Messages](messages.md)
|
|
114
|
+
|
|
115
|
+
- :material-robot:{ .lg .middle } **Agents**
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
Understand agent registration and the handler pattern
|
|
120
|
+
|
|
121
|
+
[:octicons-arrow-right-24: Agents](agents.md)
|
|
122
|
+
|
|
123
|
+
- :material-play-circle:{ .lg .middle } **Runs**
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
Explore run lifecycle, status, and execution modes
|
|
128
|
+
|
|
129
|
+
[:octicons-arrow-right-24: Runs](runs.md)
|
|
130
|
+
|
|
131
|
+
- :material-history:{ .lg .middle } **Sessions**
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
Master stateful interactions and conversation history
|
|
136
|
+
|
|
137
|
+
[:octicons-arrow-right-24: Sessions](sessions.md)
|
|
138
|
+
|
|
139
|
+
- :material-lightning-bolt:{ .lg .middle } **Events**
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
Handle real-time streaming updates
|
|
144
|
+
|
|
145
|
+
[:octicons-arrow-right-24: Events](events.md)
|
|
146
|
+
|
|
147
|
+
</div>
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# Messages
|
|
2
|
+
|
|
3
|
+
Messages are the fundamental unit of communication in ACP. They carry content between users and agents.
|
|
4
|
+
|
|
5
|
+
## Message Structure
|
|
6
|
+
|
|
7
|
+
Every message has two essential properties:
|
|
8
|
+
|
|
9
|
+
- **role**: Either `"user"` or `"agent"`
|
|
10
|
+
- **parts**: An array of content parts
|
|
11
|
+
|
|
12
|
+
```ruby
|
|
13
|
+
message = SimpleAcp::Models::Message.new(
|
|
14
|
+
role: "user",
|
|
15
|
+
parts: [
|
|
16
|
+
SimpleAcp::Models::MessagePart.text("Hello!")
|
|
17
|
+
]
|
|
18
|
+
)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Creating Messages
|
|
22
|
+
|
|
23
|
+
### Factory Methods
|
|
24
|
+
|
|
25
|
+
The simplest way to create messages:
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
# User message with text
|
|
29
|
+
user_msg = SimpleAcp::Models::Message.user("What's the weather?")
|
|
30
|
+
|
|
31
|
+
# Agent message with text
|
|
32
|
+
agent_msg = SimpleAcp::Models::Message.agent("It's sunny and 72°F")
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Multiple Parts
|
|
36
|
+
|
|
37
|
+
Messages can contain multiple content parts:
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
message = SimpleAcp::Models::Message.user(
|
|
41
|
+
SimpleAcp::Models::MessagePart.text("Here's my data:"),
|
|
42
|
+
SimpleAcp::Models::MessagePart.json({ values: [1, 2, 3] })
|
|
43
|
+
)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### From Hash
|
|
47
|
+
|
|
48
|
+
Parse messages from JSON/hash data:
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
message = SimpleAcp::Models::Message.from_hash({
|
|
52
|
+
"role" => "user",
|
|
53
|
+
"parts" => [
|
|
54
|
+
{ "content_type" => "text/plain", "content" => "Hello" }
|
|
55
|
+
]
|
|
56
|
+
})
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Message Parts
|
|
60
|
+
|
|
61
|
+
Each part has a content type and content payload.
|
|
62
|
+
|
|
63
|
+
### Text Content
|
|
64
|
+
|
|
65
|
+
Plain text is the most common content type:
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
part = SimpleAcp::Models::MessagePart.text("Hello, world!")
|
|
69
|
+
|
|
70
|
+
part.content_type # => "text/plain"
|
|
71
|
+
part.content # => "Hello, world!"
|
|
72
|
+
part.text? # => true
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### JSON Content
|
|
76
|
+
|
|
77
|
+
Structured data as JSON:
|
|
78
|
+
|
|
79
|
+
```ruby
|
|
80
|
+
part = SimpleAcp::Models::MessagePart.json({
|
|
81
|
+
temperature: 72,
|
|
82
|
+
conditions: "sunny",
|
|
83
|
+
humidity: 45
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
part.content_type # => "application/json"
|
|
87
|
+
part.content # => "{\"temperature\":72,...}"
|
|
88
|
+
part.json? # => true
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Image Content
|
|
92
|
+
|
|
93
|
+
Base64-encoded images:
|
|
94
|
+
|
|
95
|
+
```ruby
|
|
96
|
+
image_data = File.read("photo.png")
|
|
97
|
+
part = SimpleAcp::Models::MessagePart.image(
|
|
98
|
+
Base64.strict_encode64(image_data),
|
|
99
|
+
mime_type: "image/png"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
part.content_type # => "image/png"
|
|
103
|
+
part.content_encoding # => "base64"
|
|
104
|
+
part.image? # => true
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### URL References
|
|
108
|
+
|
|
109
|
+
Reference external content by URL:
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
part = SimpleAcp::Models::MessagePart.from_url(
|
|
113
|
+
"https://example.com/image.jpg",
|
|
114
|
+
content_type: "image/jpeg"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
part.content_url # => "https://example.com/image.jpg"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Accessing Content
|
|
121
|
+
|
|
122
|
+
### Text Content
|
|
123
|
+
|
|
124
|
+
Get combined text from all parts:
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
message = SimpleAcp::Models::Message.user(
|
|
128
|
+
SimpleAcp::Models::MessagePart.text("Hello "),
|
|
129
|
+
SimpleAcp::Models::MessagePart.text("World!")
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
message.text_content # => "Hello World!"
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Individual Parts
|
|
136
|
+
|
|
137
|
+
Access parts directly:
|
|
138
|
+
|
|
139
|
+
```ruby
|
|
140
|
+
message.parts.each do |part|
|
|
141
|
+
puts "Type: #{part.content_type}"
|
|
142
|
+
puts "Content: #{part.content}"
|
|
143
|
+
end
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Type Checking
|
|
147
|
+
|
|
148
|
+
Check content types:
|
|
149
|
+
|
|
150
|
+
```ruby
|
|
151
|
+
part.text? # Is text/plain?
|
|
152
|
+
part.json? # Is application/json?
|
|
153
|
+
part.image? # Is image/*?
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Serialization
|
|
157
|
+
|
|
158
|
+
Messages can be serialized to JSON:
|
|
159
|
+
|
|
160
|
+
```ruby
|
|
161
|
+
message = SimpleAcp::Models::Message.user("Hello")
|
|
162
|
+
|
|
163
|
+
# To hash
|
|
164
|
+
hash = message.to_h
|
|
165
|
+
# => { role: "user", parts: [...] }
|
|
166
|
+
|
|
167
|
+
# To JSON
|
|
168
|
+
json = message.to_json
|
|
169
|
+
# => '{"role":"user","parts":[...]}'
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Common Patterns
|
|
173
|
+
|
|
174
|
+
### Echo Agent
|
|
175
|
+
|
|
176
|
+
```ruby
|
|
177
|
+
server.agent("echo") do |context|
|
|
178
|
+
context.input.map do |msg|
|
|
179
|
+
SimpleAcp::Models::Message.agent(msg.text_content)
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Transform Content
|
|
185
|
+
|
|
186
|
+
```ruby
|
|
187
|
+
server.agent("uppercase") do |context|
|
|
188
|
+
text = context.input.first&.text_content || ""
|
|
189
|
+
SimpleAcp::Models::Message.agent(text.upcase)
|
|
190
|
+
end
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Multi-Part Response
|
|
194
|
+
|
|
195
|
+
```ruby
|
|
196
|
+
server.agent("analysis") do |context|
|
|
197
|
+
SimpleAcp::Models::Message.agent(
|
|
198
|
+
SimpleAcp::Models::MessagePart.text("Analysis complete:"),
|
|
199
|
+
SimpleAcp::Models::MessagePart.json({
|
|
200
|
+
word_count: 150,
|
|
201
|
+
sentiment: "positive"
|
|
202
|
+
})
|
|
203
|
+
)
|
|
204
|
+
end
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Next Steps
|
|
208
|
+
|
|
209
|
+
- Learn about [Agents](agents.md) that process messages
|
|
210
|
+
- Understand [Runs](runs.md) that execute agent logic
|
|
211
|
+
- Explore [Events](events.md) for streaming message delivery
|