Class: Brute::Completion::OpenRouter
- Inherits:
-
Object
- Object
- Brute::Completion::OpenRouter
- Includes:
- Hooks
- Defined in:
- lib/brute/completion/open_router.rb
Constant Summary
Constants included from Hooks
Hooks::AFTER_LLM_EVENT, Hooks::AFTER_TOOL_EVENT, Hooks::APPROVE_TOOL_EVENT, Hooks::BEFORE_LLM_EVENT, Hooks::BEFORE_TOOL_EVENT, Hooks::COMPACTED_EVENT, Hooks::COMPACT_DURATION_EVENT, Hooks::DURATION_EVENT, Hooks::ENTER_EVENT, Hooks::EXIT_EVENT, Hooks::FARADAY_ERROR_EVENT, Hooks::LLM_DURATION_EVENT, Hooks::LLM_FAILURE_EVENT, Hooks::MIDDLEWARE_ADDED_EVENT, Hooks::OPEN_ROUTER_SERVER_ERROR_EVENT, Hooks::STANDARD_ERROR_EVENT, Hooks::TOOL_DURATION_EVENT, Hooks::TURN_DURATION_EVENT, Hooks::TURN_END_EVENT, Hooks::TURN_START_EVENT
Instance Method Summary collapse
Methods included from Hooks
Constructor Details
#initialize(config: {}, **options) ⇒ OpenRouter
Anything not given here falls back to the turn env, the way the other completions do it: tools from env — the list the ToolPipeline middleware puts there and executes, so a pipeline declares its tools once — and the model from env, which lets a middleware route a turn to a different one. Options given at point of use always win.
config: keyword arguments for OpenRouter::Client.new (access_token:, request_timeout:, uri_base:, extra_headers:). Defaults to OpenRouter.configuration's global settings. options: keyword arguments for OpenRouter::CompletionOptions.new (model:, temperature:, tools:, ...).
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/brute/completion/open_router.rb', line 29 def initialize(config: {}, **) # Brute depends on no LLM library: the provider gem is required here, # at point of use, and only for this completion. begin require "open_router" rescue LoadError raise LoadError, "#{self.class} needs the 'open_router_enhanced' gem — add `gem \"open_router_enhanced\"` to your Gemfile." end @config = config # CompletionOptions defaults the model to "openrouter/auto", so what # was actually asked for is remembered before that default hides it. @model_given = .key?(:model) @options = ::OpenRouter::CompletionOptions.new(**) end |
Instance Method Details
#call(env) ⇒ Object
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/brute/completion/open_router.rb', line 45 def call(env) emit(BEFORE_LLM_EVENT, env) = Brute::MessageTransport::OpenRouter.dump_all(env[:messages]) ::OpenRouter::Client.new(**@config).then do |client| response = nil emit(LLM_DURATION_EVENT, env) { response = client.complete(, (env)) } response.then do |response| # Expose the provider's usage for downstream accounting # (goal budgets, autonomous limits, compaction thresholds, usage # attribution) — additive metadata only, normalised so a reader # does not have to know which provider answered. if (usage = Brute::MessageTransport::OpenRouter.usage_metrics(response)) (env[:metadata] ||= {})[:last_llm_usage] = usage end # OpenRouter in fact only returns a single message... # https://github.com/estiens/open_router_enhanced/blob/main/lib/open_router/response.rb Brute::MessageTransport::OpenRouter.wrap_each(response) do || env[:messages] << end end end emit(AFTER_LLM_EVENT, env) env # A provider call that raises is reported through the hooks rather than # up the stack: :llm_failure with the turn env, then one hook naming the # kind of failure. The classes are looked up defensively — the provider # gem is only a dependency of the app that uses this middleware. rescue => error emit(LLM_FAILURE_EVENT, env) if defined?(::Faraday::Error) && error.is_a?(::Faraday::Error) emit(FARADAY_ERROR_EVENT, env, error) elsif defined?(::OpenRouter::ServerError) && error.is_a?(::OpenRouter::ServerError) emit(OPEN_ROUTER_SERVER_ERROR_EVENT, env, error) else emit(STANDARD_ERROR_EVENT, env, error) end env end |