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 |
# File 'lib/brute/rack/adapter.rb', line 40 def initialize(agent) raise ArgumentError, "agent must respond to #start" unless agent.respond_to?(:start) @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.
49 50 51 52 53 54 55 56 57 |
# File 'lib/brute/rack/adapter.rb', line 49 def call(env) prompt = prompt_from(env) return response_for(env, 400, "No prompt provided.") if prompt.nil? || prompt.empty? turn = @agent.start(prompt) response_for(env, 200, output_of(turn)) 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.
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/brute/rack/adapter.rb', line 67 def prompt_from(env) request = ::Rack::Request.new(env) if (query = request.GET["prompt"]) && !query.empty? return query end body = read_body(request) return nil if body.nil? || body.strip.empty? if json?(request.media_type) case data = parse_json(body) when ::Hash then return PROMPT_KEYS.filter_map { |key| data[key] }.first&.to_s || body when ::String then return data end end if request.form_data? && (field = ::Rack::Utils.parse_nested_query(body)["prompt"]) return field end body 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.
95 96 97 98 99 100 101 102 103 104 |
# File 'lib/brute/rack/adapter.rb', line 95 def response_for(env, status, output) text = output.to_s if wants_json?(env) key = status == 200 ? :response : :error [status, {"content-type" => "application/json"}, [::JSON.generate(key => text)]] else [status, {"content-type" => "text/plain; charset=utf-8"}, [text]] end end |