Class: Nanobot::Providers::RubyLLMProvider

Inherits:
LLMProvider
  • Object
show all
Defined in:
lib/nanobot/providers/rubyllm_provider.rb

Overview

LLM provider implementation backed by the RubyLLM gem. Supports all RubyLLM backends (Anthropic, OpenAI, Gemini, DeepSeek, OpenRouter, Mistral, Perplexity, xAI, Ollama, GPUStack, Bedrock, etc.).

Defined Under Namespace

Classes: ToolCallProxy

Constant Summary collapse

PROVIDER_KEY_MAP =

Maps nanobot provider names to their RubyLLM api_key config attribute. Providers not listed here (or mapped to nil) have no api_key setting.

{
  'anthropic' => :anthropic_api_key,
  'openai' => :openai_api_key,
  'gemini' => :gemini_api_key,
  'deepseek' => :deepseek_api_key,
  'openrouter' => :openrouter_api_key,
  'mistral' => :mistral_api_key,
  'perplexity' => :perplexity_api_key,
  'xai' => :xai_api_key,
  'gpustack' => :gpustack_api_key,
  'bedrock' => :bedrock_api_key,
  'groq' => :openai_api_key
}.freeze
PROVIDER_BASE_MAP =

Maps nanobot provider names to their RubyLLM api_base config attribute. Only providers that support a custom base URL are listed.

{
  'openai' => :openai_api_base,
  'gemini' => :gemini_api_base,
  'ollama' => :ollama_api_base,
  'gpustack' => :gpustack_api_base
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(api_key: nil, api_base: nil, default_model: nil, provider: 'anthropic', logger: nil) ⇒ RubyLLMProvider

Returns a new instance of RubyLLMProvider.

Parameters:

  • api_key (String, nil) (defaults to: nil) —

    API key for the provider

  • api_base (String, nil) (defaults to: nil) —

    custom API base URL

  • default_model (String, nil) (defaults to: nil) —

    model identifier (defaults to claude-haiku-4-5)

  • provider (String) (defaults to: 'anthropic') —

    provider backend name (anthropic, openai, deepseek, groq, openrouter)

  • logger (Logger, nil) (defaults to: nil) —

    logger instance (defaults to null logger)



49
50
51
52
53
54
55
56
57
58
59
# File 'lib/nanobot/providers/rubyllm_provider.rb', line 49

def initialize(api_key: nil, api_base: nil, default_model: nil, provider: 'anthropic', logger: nil)
  super()
  @api_key = api_key
  @api_base = api_base
  @provider = provider.to_s
  @default_model = default_model || 'claude-haiku-4-5'
  @logger = logger || Logger.new(IO::NULL)
  @chat = nil

  configure_client
end

Instance Attribute Details

#default_model ⇒ Object (readonly)

Returns the value of attribute default_model.



42
43
44
# File 'lib/nanobot/providers/rubyllm_provider.rb', line 42

def default_model
  @default_model
end

#logger ⇒ Object (readonly)

Returns the value of attribute logger.



42
43
44
# File 'lib/nanobot/providers/rubyllm_provider.rb', line 42

def logger
  @logger
end

Instance Method Details

#chat(messages:, tools: nil, model: nil, max_tokens: 4096, temperature: 0.7) ⇒ LLMResponse

Chat with tools using RubyLLM's provider API directly This bypasses RubyLLM's automatic tool execution to give us control

Parameters:

  • messages (Array<Hash>) —

    message history with system, user, assistant roles

  • tools (Array<RubyLLM::Tool>) (defaults to: nil) —

    array of tool instances

  • model (String, nil) (defaults to: nil) —

    model to use

  • max_tokens (Integer) (defaults to: 4096) —

    max tokens (not used currently)

  • temperature (Float) (defaults to: 0.7) —

    sampling temperature

Returns:

  • (LLMResponse) —

    response with content and tool calls



69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/nanobot/providers/rubyllm_provider.rb', line 69

def chat(messages:, tools: nil, model: nil, max_tokens: 4096, temperature: 0.7)
  model_to_use = model || @default_model
  log_request(model_to_use, temperature, max_tokens, messages, tools)

  response = call_provider(model_to_use, messages, tools, temperature)
  log_response(response)

  LLMResponse.new(content: response.content, tool_calls: convert_tool_calls(response), finish_reason: 'stop')
rescue StandardError => e
  @logger.error "Error calling LLM: #{e.message}"
  @logger.error e.backtrace.join("\n")

  LLMResponse.new(content: "Error calling LLM: #{e.message}", tool_calls: [], finish_reason: 'error')
end