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: 600, read_timeout: nil, connect_timeout: 5, 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: 600)

    The number of seconds to wait for a response. Also serves as the default read timeout.

  • read_timeout (Integer, nil) (defaults to: nil)

    The number of seconds to wait for a response. Defaults to timeout.

  • connect_timeout (Integer) (defaults to: 5)

    The number of seconds to wait for a TCP connection to open.

  • 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.



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/llm/provider.rb', line 35

def initialize(key:, host:, port: 443, timeout: 600, read_timeout: nil, connect_timeout: 5, ssl: true, base_path: "", persistent: false, transport: nil)
  @key = key
  @host = host
  @port = port
  @read_timeout = read_timeout || timeout
  @timeout = @read_timeout
  @connect_timeout = connect_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}"}
  @monitor = Monitor.new
  @transport = LLM::Transport::Utils.resolve_transport(
    host:,
    port:,
    timeout: @read_timeout,
    connect_timeout: @connect_timeout,
    ssl:,
    transport:,
    persistent:
  )
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)


425
426
427
# File 'lib/llm/provider.rb', line 425

def adapt_function(fn)
  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)


247
248
249
# File 'lib/llm/provider.rb', line 247

def assistant_role
  raise NotImplementedError
end

#audioLLM::OpenAI::Audio

Returns an interface to the audio API

Returns:

Raises:

  • (NotImplementedError)


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

def audio
  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:



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/llm/provider.rb', line 77

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

#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:



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

def chat(prompt, params = {})
  role = params.delete(:role)
  LLM::Context.new(self, params).talk(prompt, role:)
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



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

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

#default_modelString

Returns the default model for chat completions

Returns:

  • (String)

    Returns the default model for chat completions

Raises:

  • (NotImplementedError)


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

def default_model
  raise NotImplementedError
end

#developer_roleSymbol

Returns:

  • (Symbol)


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

def developer_role
  :developer
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



129
130
131
# File 'lib/llm/provider.rb', line 129

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

#filesLLM::OpenAI::Files

Returns an interface to the files API

Returns:

Raises:

  • (NotImplementedError)


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

def files
  raise NotImplementedError
end

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

Returns an interface to the images API

Returns:

Raises:

  • (NotImplementedError)


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

def images
  raise NotImplementedError
end

#inspectString

Note:

The secret key is redacted in inspect for security reasons

Returns an inspection of the provider object

Returns:

  • (String)


98
99
100
# File 'lib/llm/provider.rb', line 98

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

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

Interrupt the active request, if any.

Parameters:

  • owner (Fiber)

Returns:

  • (nil)


399
400
401
# File 'lib/llm/provider.rb', line 399

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

#key?Boolean

Returns true when an API key is configured

Returns:

  • (Boolean)

    Returns true when an API key is configured



415
416
417
# File 'lib/llm/provider.rb', line 415

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

#modelsLLM::OpenAI::Models

Returns an interface to the models API

Returns:

Raises:

  • (NotImplementedError)


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

def models
  raise NotImplementedError
end

#moderationsLLM::OpenAI::Moderations

Returns an interface to the moderations API

Returns:

Raises:

  • (NotImplementedError)


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

def moderations
  raise NotImplementedError
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



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

def name
  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)


139
140
141
# File 'lib/llm/provider.rb', line 139

def ocr(...)
  raise NotImplementedError
end

#registryLLM::Registry

Returns the provider's model registry.

Returns:



114
115
116
# File 'lib/llm/provider.rb', line 114

def registry
  LLM.registry_for(self)
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:



408
409
410
# File 'lib/llm/provider.rb', line 408

def request_owner
  transport.request_owner
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



185
186
187
188
# File 'lib/llm/provider.rb', line 185

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)


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

def responses
  raise NotImplementedError
end

#schemaLLM::Schema

Returns an object that can generate a JSON schema

Returns:



261
262
263
# File 'lib/llm/provider.rb', line 261

def schema
  LLM::Schema.new
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:



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

def server_tool(name, options = {})
  LLM::ServerTool.new(name, options, self)
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:



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

def server_tools
  {}
end

#system_roleSymbol

Returns:

  • (Symbol)


332
333
334
# File 'lib/llm/provider.rb', line 332

def system_role
  :system
end

#tool_roleSymbol

Returns:

  • (Symbol)


344
345
346
# File 'lib/llm/provider.rb', line 344

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



351
352
353
# File 'lib/llm/provider.rb', line 351

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

#tracer=(tracer) ⇒ void

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:



365
366
367
# File 'lib/llm/provider.rb', line 365

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

#user_roleSymbol

Returns:

  • (Symbol)


326
327
328
# File 'lib/llm/provider.rb', line 326

def user_role
  :user
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)


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

def vector_stores
  raise NotImplementedError
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



320
321
322
# File 'lib/llm/provider.rb', line 320

def web_search(query:)
  raise NotImplementedError
end

#with(**headers) ⇒ LLM::Provider

Note:

For backwards compatibility, headers can be provided via the headers: keyword argument, or provided directly as a Hash without the headers: key namespace.

Add one or more headers to all requests

Examples:

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

Parameters:

  • headers (Hash<String,String>)

    One or more headers

Returns:



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

def with(**headers)
  headers = headers.merge(headers.delete(:headers) || {})
  lock do
    tap { @headers.merge!(headers) }
  end
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:



380
381
382
383
384
385
386
387
388
389
390
391
392
393
# File 'lib/llm/provider.rb', line 380

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