Class: LLM::Provider Abstract

Inherits:
Object
  • Object
show all
Includes:
Transport::Execution
Defined in:
lib/llm/provider.rb

Overview

This class is abstract.

The Provider class is the abstract base for LLM service integrations. Most users interact with providers through Agent or Context rather than calling #complete directly.

Direct Known Subclasses

Anthropic, Bedrock, Google, Ollama, OpenAI

Instance Method Summary collapse

Constructor Details

#initialize(key:, host:, port: 443, timeout: 900, ssl: true, base_path: "", persistent: false, transport: nil) ⇒ Provider

Returns a new instance of Provider.

Parameters:

  • key (String, nil)

    The secret key for authentication

  • host (String)

    The host address of the LLM provider

  • port (Integer) (defaults to: 443)

    The port number

  • timeout (Integer) (defaults to: 900)

    The number of seconds to wait for a response

  • ssl (Boolean) (defaults to: true)

    Whether to use SSL for the connection

  • base_path (String) (defaults to: "")

    Optional base path prefix for HTTP API routes.

  • persistent (Boolean) (defaults to: false)

    Whether to use a persistent connection. Requires the net-http-persistent gem.

  • transport (LLM::Transport, Class, nil) (defaults to: nil)

    Optional override with any Transport instance or subclass.



30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/llm/provider.rb', line 30

def initialize(key:, host:, port: 443, timeout: 900, ssl: true, base_path: "", persistent: false, transport: nil)
  @key = key
  @host = host
  @port = port
  @timeout = timeout
  @ssl = ssl
  @base_path = LLM::Utils.normalize_base_path(base_path)
  @base_uri = URI("#{ssl ? "https" : "http"}://#{host}:#{port}/")
  @headers = {"User-Agent" => "llm.rb v#{LLM::VERSION}"}
  @transport = LLM::Transport::Utils.resolve_transport(host:, port:, timeout:, ssl:, transport:, persistent:)
  @monitor = Monitor.new
end

Instance Method Details

#adapt_function(fn) ⇒ Hash

This method is abstract.

Adapt a Function to the provider-specific tool schema.

Parameters:

Returns:

  • (Hash)

Raises:

  • (NotImplementedError)


397
398
399
# File 'lib/llm/provider.rb', line 397

def adapt_function(fn)
  raise NotImplementedError
end

#build_messages(prompt, params, role, key: :messages) ⇒ Array<LLM::Message>

Builds the outgoing message array for a turn. Normalizes the prompt into one or more Message objects and prepends the existing history.

The method is idempotent. If the prompt is already an Message or an array of Messages (ie it was built by a previous call and possibly transformed), it is returned as-is without rebuilding.

Parameters:

  • prompt (String, Array, LLM::Message, LLM::Prompt)
  • params (Hash)

    Turn params. The history is taken from params[:messages].

  • role (Symbol)

    The role to assign to a raw prompt

  • key (Symbol) (defaults to: :messages)

    The params key that holds the history (:messages for chat completions, :input for the responses API).

Returns:



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/llm/provider.rb', line 62

def build_messages(prompt, params, role, key: :messages)
  case prompt
  when LLM::Message
    [prompt]
  when Array
    if prompt.all? { LLM::Message === _1 }
      prompt
    else
      [*(params.delete(key) || []), LLM::Message.new(role, prompt)]
    end
  when LLM::Prompt
    [*(params.delete(key) || []), *prompt.to_a]
  else
    [*(params.delete(key) || []), LLM::Message.new(role, prompt)]
  end
end

#inspectString

Note:

The secret key is redacted in inspect for security reasons

Returns an inspection of the provider object

Returns:

  • (String)


83
84
85
# File 'lib/llm/provider.rb', line 83

def inspect
  "#<#{LLM::Utils.object_id(self)} @key=[REDACTED] @transport=#{transport.inspect} @tracer=#{tracer.inspect}>"
end

#nameSymbol

Returns the provider's name

Returns:

  • (Symbol)

    Returns the provider's name

Raises:

  • (NotImplementedError)

    When the method is not implemented by a subclass



92
93
94
# File 'lib/llm/provider.rb', line 92

def name
  raise NotImplementedError
end

#embed(input, model: nil, **params) ⇒ LLM::Response

Provides an embedding

Parameters:

  • input (String, Array<String>)

    The input to embed

  • model (String) (defaults to: nil)

    The embedding model to use

  • params (Hash)

    Other embedding parameters

Returns:

Raises:

  • (NotImplementedError)

    When the method is not implemented by a subclass



107
108
109
# File 'lib/llm/provider.rb', line 107

def embed(input, model: nil, **params)
  raise NotImplementedError
end

#ocrLLM::Response

Note:

This feature is not implemented by all providers, and it will raise NotImplementedError for providers that do not support it.

Returns:

Raises:

  • (NotImplementedError)


117
118
119
# File 'lib/llm/provider.rb', line 117

def ocr(...)
  raise NotImplementedError
end

#complete(prompt, params = {}) ⇒ LLM::Response

Provides an interface to the chat completions API. Most users should use Context#talk or Agent#talk instead.

Examples:

llm = LLM.openai(key: ENV["KEY"])
messages = [{role: "system", content: "Your task is to answer all of my questions"}]
res = llm.complete("5 + 2 ?", messages:)
print "[#{res.messages[0].role}]", res.messages[0].content, "\n"

Parameters:

  • prompt (String)

    The input prompt to be completed

  • params (Hash) (defaults to: {})

    The parameters to maintain throughout the conversation. Any parameter the provider supports can be included and not only those listed here.

Options Hash (params):

  • :role (Symbol)

    Defaults to the provider's default role

  • :model (String)

    Defaults to the provider's default model

  • :schema (#to_json, nil)

    Defaults to nil

  • :tools (Array<LLM::Function>, nil)

    Defaults to nil

Returns:

Raises:

  • (NotImplementedError)

    When the method is not implemented by a subclass



143
144
145
# File 'lib/llm/provider.rb', line 143

def complete(prompt, params = {})
  raise NotImplementedError
end

#chat(prompt, params = {}) ⇒ LLM::Context

Starts a new chat powered by the chat completions API

Parameters:

  • prompt (String)

    The input prompt to be completed

  • params (Hash) (defaults to: {})

    The parameters to maintain throughout the conversation. Any parameter the provider supports can be included and not only those listed here.

Returns:



152
153
154
155
# File 'lib/llm/provider.rb', line 152

def chat(prompt, params = {})
  role = params.delete(:role)
  LLM::Context.new(self, params).talk(prompt, role:)
end

#respond(prompt, params = {}) ⇒ LLM::Context

Starts a new chat powered by the responses API

Parameters:

  • prompt (String)

    The input prompt to be completed

  • params (Hash) (defaults to: {})

    The parameters to maintain throughout the conversation. Any parameter the provider supports can be included and not only those listed here.

Returns:

Raises:

  • (NotImplementedError)

    When the method is not implemented by a subclass



163
164
165
166
# File 'lib/llm/provider.rb', line 163

def respond(prompt, params = {})
  role = params.delete(:role)
  LLM::Context.new(self, params).respond(prompt, role:)
end

#responsesLLM::OpenAI::Responses

Note:

Compared to the chat completions API, the responses API can require less bandwidth on each turn, maintain state server-side, and produce faster responses.

Returns:

Raises:

  • (NotImplementedError)


175
176
177
# File 'lib/llm/provider.rb', line 175

def responses
  raise NotImplementedError
end

#imagesLLM::OpenAI::Images, LLM::Google::Images

Returns an interface to the images API

Returns:

Raises:

  • (NotImplementedError)


182
183
184
# File 'lib/llm/provider.rb', line 182

def images
  raise NotImplementedError
end

#audioLLM::OpenAI::Audio

Returns an interface to the audio API

Returns:

Raises:

  • (NotImplementedError)


189
190
191
# File 'lib/llm/provider.rb', line 189

def audio
  raise NotImplementedError
end

#filesLLM::OpenAI::Files

Returns an interface to the files API

Returns:

Raises:

  • (NotImplementedError)


196
197
198
# File 'lib/llm/provider.rb', line 196

def files
  raise NotImplementedError
end

#modelsLLM::OpenAI::Models

Returns an interface to the models API

Returns:

Raises:

  • (NotImplementedError)


203
204
205
# File 'lib/llm/provider.rb', line 203

def models
  raise NotImplementedError
end

#moderationsLLM::OpenAI::Moderations

Returns an interface to the moderations API

Returns:

Raises:

  • (NotImplementedError)


210
211
212
# File 'lib/llm/provider.rb', line 210

def moderations
  raise NotImplementedError
end

#vector_storesLLM::OpenAI::VectorStore

Returns an interface to the vector stores API

Returns:

  • (LLM::OpenAI::VectorStore)

    Returns an interface to the vector stores API

Raises:

  • (NotImplementedError)


217
218
219
# File 'lib/llm/provider.rb', line 217

def vector_stores
  raise NotImplementedError
end

#assistant_roleString

Returns the role of the assistant in the conversation. Usually "assistant" or "model"

Returns:

  • (String)

    Returns the role of the assistant in the conversation. Usually "assistant" or "model"

Raises:

  • (NotImplementedError)


225
226
227
# File 'lib/llm/provider.rb', line 225

def assistant_role
  raise NotImplementedError
end

#default_modelString

Returns the default model for chat completions

Returns:

  • (String)

    Returns the default model for chat completions

Raises:

  • (NotImplementedError)


232
233
234
# File 'lib/llm/provider.rb', line 232

def default_model
  raise NotImplementedError
end

#schemaLLM::Schema

Returns an object that can generate a JSON schema

Returns:



239
240
241
# File 'lib/llm/provider.rb', line 239

def schema
  LLM::Schema.new
end

#with(headers:) ⇒ LLM::Provider

Add one or more headers to all requests

Examples:

llm = LLM.openai(key: ENV["KEY"])
llm.with(headers: {"OpenAI-Organization" => ENV["ORG"]})
llm.with(headers: {"OpenAI-Project" => ENV["PROJECT"]})

Parameters:

  • headers (Hash<String,String>)

    One or more headers

Returns:



253
254
255
256
257
# File 'lib/llm/provider.rb', line 253

def with(headers:)
  lock do
    tap { @headers.merge!(headers) }
  end
end

#server_toolsString => LLM::ServerTool

Note:

This method might be outdated, and the LLM::Provider#server_tool method can be used if a tool is not found here.

Returns all known tools provided by a provider.

Returns:



265
266
267
# File 'lib/llm/provider.rb', line 265

def server_tools
  {}
end

#server_tool(name, options = {}) ⇒ LLM::ServerTool

Note:

OpenAI, Anthropic, and Gemini provide platform-tools for things like web search, and more.

Returns a tool provided by a provider.

Examples:

llm   = LLM.openai(key: ENV["KEY"])
tools = [llm.server_tool(:web_search)]
res   = llm.responses.create("Summarize today's news", tools:)
print res.output_text, "\n"

Parameters:

  • name (String, Symbol)

    The name of the tool

  • options (Hash) (defaults to: {})

    Configuration options for the tool

Returns:



282
283
284
# File 'lib/llm/provider.rb', line 282

def server_tool(name, options = {})
  LLM::ServerTool.new(name, options, self)
end

#web_search(query:) ⇒ LLM::Response

Provides a web search capability

Parameters:

  • query (String)

    The search query

Returns:

Raises:

  • (NotImplementedError)

    When the method is not implemented by a subclass



292
293
294
# File 'lib/llm/provider.rb', line 292

def web_search(query:)
  raise NotImplementedError
end

#user_roleSymbol

Returns:

  • (Symbol)


298
299
300
# File 'lib/llm/provider.rb', line 298

def user_role
  :user
end

#system_roleSymbol

Returns:

  • (Symbol)


304
305
306
# File 'lib/llm/provider.rb', line 304

def system_role
  :system
end

#developer_roleSymbol

Returns:

  • (Symbol)


310
311
312
# File 'lib/llm/provider.rb', line 310

def developer_role
  :developer
end

#tool_roleSymbol

Returns:

  • (Symbol)


316
317
318
# File 'lib/llm/provider.rb', line 316

def tool_role
  :tool
end

#tracerLLM::Tracer

Returns the current scoped tracer override or provider default tracer

Returns:

  • (LLM::Tracer)

    Returns the current scoped tracer override or provider default tracer



323
324
325
# File 'lib/llm/provider.rb', line 323

def tracer
  weakmap[self] || @tracer || LLM::Tracer::Null.new(self)
end

#tracer=(tracer)

This method returns an undefined value.

Set the provider's default tracer This tracer is shared by the provider instance and becomes the fallback whenever no scoped override is active.

Examples:

llm = LLM.openai(key: ENV["KEY"])
llm.tracer = LLM::Tracer::Logger.new(llm, path: "/path/to/log.txt")

Parameters:



337
338
339
# File 'lib/llm/provider.rb', line 337

def tracer=(tracer)
  @tracer = tracer || LLM::Tracer::Null.new(self)
end

#with_tracer(tracer) { ... } ⇒ Object

Override the tracer for the current fiber while the block runs. This is useful when you want per-request or per-turn tracing without replacing the provider's default tracer.

Examples:

llm.with_tracer(LLM::Tracer::Logger.new(llm, io: $stdout)) do
  llm.complete("hello", model: "gpt-5.4-mini")
end

Parameters:

Yields:

Returns:



352
353
354
355
356
357
358
359
360
361
362
363
364
365
# File 'lib/llm/provider.rb', line 352

def with_tracer(tracer)
  had_override = weakmap.key?(self)
  previous = weakmap[self]
  weakmap[self] = tracer || LLM::Tracer::Null.new(self)
  yield
ensure
  if had_override
    weakmap[self] = previous
  elsif weakmap.respond_to?(:delete)
    weakmap.delete(self)
  else
    weakmap[self] = nil
  end
end

#interrupt!(owner) ⇒ nil Also known as: cancel!

Interrupt the active request, if any.

Parameters:

  • owner (Fiber)

Returns:

  • (nil)


371
372
373
# File 'lib/llm/provider.rb', line 371

def interrupt!(owner)
  transport.interrupt!(owner)
end

#request_ownerObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the current request owner used by the transport.

Returns:



380
381
382
# File 'lib/llm/provider.rb', line 380

def request_owner
  transport.request_owner
end

#key?Boolean

Returns true when an API key is configured

Returns:

  • (Boolean)

    Returns true when an API key is configured



387
388
389
# File 'lib/llm/provider.rb', line 387

def key?
  @key != nil && @key.to_s.strip.size > 0
end