Serving over HTTP
An AgentPipeline is a builder, not a Rack app — you .start it. Brute::Rack::Adapter bridges the gap: it wraps an agent as a Rack app (call(env) → [status, headers, body]), so any agent drops into a config.ru and serves over HTTP behind Falcon, Puma, or anything Rack.
# config.ru
require "brute"
agent = Brute::Turn::AgentPipeline.parse_file("examples/agents/brute.ru")
run Brute::Rack::Adapter.for(agent)
$ curl -d 'What files are here?' localhost:9292
$ curl -H 'content-type: application/json' -d '{"prompt":"hi"}' localhost:9292
$ curl 'localhost:9292/?prompt=hi'
How the prompt is extracted
The adapter looks for the prompt in four places, most explicit first:
- a
?prompt=query parameter, - a JSON body — a
prompt/message/inputkey of an object, or a bare JSON string, - a form-encoded
prompt=field, - otherwise the raw request body is the prompt.
A missing prompt is a 400; anything the turn raises is a 500. Responses are content-negotiated: a JSON request (or Accept: application/json) gets {"response": "..."}, everything else gets text/plain.
The two transforms
The whole adapter is two pure functions wired around one agent.start:
env -> prompt string (the request half)
output -> [status, headers, body] (the response half)
The agent’s answer is the last message it appended to the log. Anything that responds to #start(prompt) -> env works — an AgentPipeline, a SubAgent, or any turn-shaped callable — so you can serve a sub-agent directly if that’s the unit you want to expose.
Agents as .ru files
parse_file reads an agent defined in rackup syntax, so the HTTP entry point and the agent definition can live in one versioned file:
# examples/agents/brute.ru
use Brute::Middleware::SystemPrompt
use Brute::Middleware::Loop::ToolResult
use Brute::Middleware::MaxIterations
use Brute::Middleware::ToolPipeline, tools: Brute::Tools::ALL
run ->(env) { ... } # your LLM call
The repo ships examples/agents/brute.ru (the agent) and examples/agents/config.ru (the Brute::Rack::Adapter.for(agent) wrapper) as a working pair — run it with rackup examples/agents/config.ru or falcon serve -c config.ru.
This is the same builder covered in The Agent Pipeline — serving it over HTTP adds no new concepts, just a Rack wrapper.