Class: Brute::Completion::OpenRouter

Inherits:
Object
  • Object
show all
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

new

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: {}, **options)
  # 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 = options.key?(:model)
  @options = ::OpenRouter::CompletionOptions.new(**options)
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)

  messages = Brute::MessageTransport::OpenRouter.dump_all(env[:messages])

  ::OpenRouter::Client.new(**@config).then do |client|
    response = nil
    emit(LLM_DURATION_EVENT, env) { response = client.complete(messages, options(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 |message|
        env[:messages] << message
      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