Sync & Async Execution¶
SimpleAcp supports both synchronous and asynchronous execution patterns for different use cases.
Synchronous Execution¶
Basic Usage¶
Wait for the run to complete:
run = client.run_sync(
agent: "echo",
input: [SimpleAcp::Models::Message.user("Hello")]
)
puts run.status # => "completed"
puts run.output # => [Message, ...]
With Session¶
client.use_session("conversation-123")
run = client.run_sync(
agent: "chat",
input: [SimpleAcp::Models::Message.user("Hello")]
)
Handling Results¶
run = client.run_sync(agent: "processor", input: [...])
case run.status
when "completed"
run.output.each do |message|
puts message.text_content
end
when "failed"
puts "Error code: #{run.error.code}"
puts "Error message: #{run.error.message}"
when "awaiting"
puts "Agent needs more input:"
puts run.await_request.message.text_content
# Handle await...
end
When to Use Sync¶
- Quick operations (< few seconds)
- Simple request-response patterns
- When you need the result immediately
- CLI tools and scripts
Asynchronous Execution¶
Basic Usage¶
Start a run without waiting:
run = client.run_async(
agent: "slow-processor",
input: [SimpleAcp::Models::Message.user("Large dataset")]
)
puts "Run started: #{run.run_id}"
puts "Status: #{run.status}" # => "in_progress"
Polling for Completion¶
run = client.run_async(agent: "processor", input: [...])
# Poll until complete
loop do
run = client.run_status(run.run_id)
puts "Status: #{run.status}"
break if run.terminal?
sleep 2
end
# Process results
puts run.output if run.completed?
puts run.error if run.failed?
With Timeout¶
run = client.run_async(agent: "processor", input: [...])
timeout = Time.now + 300 # 5 minutes
loop do
run = client.run_status(run.run_id)
break if run.terminal?
if Time.now > timeout
client.run_cancel(run.run_id)
raise "Operation timed out"
end
sleep 2
end
When to Use Async¶
- Long-running operations
- Background processing
- When you can poll for results
- Fire-and-forget patterns
Comparison¶
| Aspect | Synchronous | Asynchronous |
|---|---|---|
| Blocking | Yes | No |
| Response time | Waits for completion | Immediate |
| Use case | Quick operations | Long operations |
| Error handling | Inline | Via polling |
Helper Patterns¶
Sync Wrapper for Async¶
def run_with_timeout(client, agent:, input:, timeout: 60)
run = client.run_async(agent: agent, input: input)
deadline = Time.now + timeout
loop do
run = client.run_status(run.run_id)
return run if run.terminal?
if Time.now > deadline
client.run_cancel(run.run_id)
raise "Timeout waiting for run"
end
sleep 1
end
end
Async with Callback¶
def run_async_with_callback(client, agent:, input:, &block)
Thread.new do
run = client.run_async(agent: agent, input: input)
loop do
run = client.run_status(run.run_id)
break if run.terminal?
sleep 1
end
block.call(run)
end
end
# Usage
run_async_with_callback(client, agent: "processor", input: [...]) do |run|
puts "Completed: #{run.output}"
end
puts "Continuing while processing..."
Batch Async Execution¶
def run_batch(client, jobs)
# Start all jobs
runs = jobs.map do |job|
client.run_async(
agent: job[:agent],
input: job[:input]
)
end
# Wait for all to complete
completed = []
until runs.empty?
runs.each do |run|
updated = client.run_status(run.run_id)
if updated.terminal?
completed << updated
runs.delete(run)
end
end
sleep 1 unless runs.empty?
end
completed
end
# Usage
results = run_batch(client, [
{ agent: "processor", input: [...] },
{ agent: "analyzer", input: [...] },
{ agent: "formatter", input: [...] }
])
Resuming Awaited Runs¶
Both sync and async support resuming:
Sync Resume¶
run = client.run_sync(agent: "questioner", input: [...])
if run.awaiting?
puts run.await_request.message.text_content
run = client.run_resume_sync(
run_id: run.run_id,
await_resume: SimpleAcp::Models::MessageAwaitResume.new(
message: SimpleAcp::Models::Message.user("My answer")
)
)
end
Async Resume¶
run = client.run_async(agent: "questioner", input: [...])
# ... later
run = client.run_status(run.run_id)
if run.awaiting?
# Resume asynchronously
run = client.run_async(
run_id: run.run_id,
await_resume: SimpleAcp::Models::MessageAwaitResume.new(
message: SimpleAcp::Models::Message.user("Answer")
)
)
end
Error Handling¶
Sync Errors¶
begin
run = client.run_sync(agent: "processor", input: [...])
if run.failed?
handle_error(run.error)
end
rescue Faraday::TimeoutError
puts "Request timed out"
rescue Faraday::ConnectionFailed
puts "Connection failed"
end
Async Errors¶
run = client.run_async(agent: "processor", input: [...])
begin
loop do
run = client.run_status(run.run_id)
break if run.terminal?
sleep 1
end
rescue => e
# Try to cancel on error
client.run_cancel(run.run_id) rescue nil
raise e
end
if run.failed?
handle_error(run.error)
end
Best Practices¶
- Choose wisely - Use sync for quick ops, async for long ones
- Set timeouts - Always have a maximum wait time
- Handle all states - Check for completed, failed, awaiting, cancelled
- Cancel on error - Clean up in-progress runs when errors occur
- Poll appropriately - Don't poll too frequently (1-2 second intervals)
Next Steps¶
- Learn about Streaming for real-time updates
- Explore Session Management
- See Events for event details