Class: Brute::Rack::Adapter
- Inherits:
-
Object
- Object
- Brute::Rack::Adapter
- Defined in:
- lib/brute/rack/adapter.rb
Constant Summary collapse
- PROMPT_KEYS =
Body/JSON keys we accept a prompt under, in priority order.
promptis canonical;message/inputare common aliases. %w[prompt message input].freeze
Class Method Summary collapse
-
.for(agent) ⇒ Object
Convenience factory so the config.ru reads
run Adapter.for(agent).
Instance Method Summary collapse
-
#call(env) ⇒ Object
Rack entry point.
-
#initialize(agent) ⇒ Adapter
constructor
A new instance of Adapter.
-
#prompt_from(env) ⇒ Object
env -> prompt string.
-
#response_for(env, status, output) ⇒ Object
output -> [status, headers, body].
Constructor Details
#initialize(agent) ⇒ Adapter
Returns a new instance of Adapter.
40 41 42 43 44 45 46 |
# File 'lib/brute/rack/adapter.rb', line 40 def initialize(agent) unless agent.respond_to?(:start) raise ArgumentError, "agent must respond to #start" end @agent = agent end |
Class Method Details
.for(agent) ⇒ Object
Convenience factory so the config.ru reads run Adapter.for(agent).
36 |
# File 'lib/brute/rack/adapter.rb', line 36 def self.for(agent) = new(agent) |
Instance Method Details
#call(env) ⇒ Object
Rack entry point. Extract the prompt, run one agent turn, render the assistant's reply back as an HTTP response. A missing prompt is a 400 (client's fault); anything the turn raises is a 500.
51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/brute/rack/adapter.rb', line 51 def call(env) prompt = prompt_from(env) if prompt.nil? || prompt.empty? response_for(env, 400, "No prompt provided.") else turn = @agent.start(prompt) response_for(env, 200, output_of(turn)) end rescue => error response_for(env, 500, error.) end |
#prompt_from(env) ⇒ Object
env -> prompt string. Four ways in, most explicit first:
1. a `?prompt=` query param (never touches the body),
2. a JSON body — a known key of an object, or a bare JSON string,
3. a form-encoded `prompt=` field,
4. otherwise the raw request body IS the prompt.
Returns nil when nothing usable is present.
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/brute/rack/adapter.rb', line 71 def prompt_from(env) request = ::Rack::Request.new(env) if (query = request.GET["prompt"]) && !query.empty? query else body = read_body(request) if body.nil? || body.strip.empty? nil else from_json = nil if json?(request.media_type) case data = parse_json(body) when ::Hash then from_json = PROMPT_KEYS.filter_map { |key| data[key] }.first&.to_s || body when ::String then from_json = data end end if from_json from_json elsif request.form_data? && (field = ::Rack::Utils.parse_nested_query(body)["prompt"]) field else body end end end end |
#response_for(env, status, output) ⇒ Object
output -> [status, headers, body]. Content-negotiated: JSON in (or an
Accept: application/json) gets {"response": "..."} back; everything
else gets text/plain. Errors ride the same path so a 500 body is
shaped like a 200 body.
104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/brute/rack/adapter.rb', line 104 def response_for(env, status, output) text = output.to_s if wants_json?(env) if status == 200 key = :response else key = :error end [status, {"content-type" => "application/json"}, [::JSON.generate(key => text)]] else [status, {"content-type" => "text/plain; charset=utf-8"}, [text]] end end |