Class: Ask::Provider

Inherits:
Object
  • Object
show all
Defined in:
lib/ask/provider.rb

Overview

Abstract base class for all LLM providers. Defines the interface that provider gems (ask-openai, ask-anthropic, etc.) must implement.

Provider gems subclass this and implement the abstract methods:

Providers register themselves via Provider.register so that Provider.resolve returns the correct class by name.

Examples:

Defining a custom provider

class MyProvider < Ask::Provider
  def api_base = "https://api.example.com/v1"
  def headers = { "Authorization" => "Bearer #{@config.api_key}" }
  def chat(messages, model:, **opts, &block) = # ...
  def embed(text, model:) = # ...
  def list_models = # ...
end
Ask::Provider.register(:my, MyProvider)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config = {}) ⇒ Provider

Returns a new instance of Provider.

Parameters:

  • config (Object) (defaults to: {})

    provider configuration (must respond to configuration_requirements)



35
36
37
38
# File 'lib/ask/provider.rb', line 35

def initialize(config = {})
  @config = config
  ensure_configured!
end

Instance Attribute Details

#configObject (readonly)

Returns the configuration object passed to the constructor.

Returns:

  • (Object)

    the configuration object passed to the constructor



32
33
34
# File 'lib/ask/provider.rb', line 32

def config
  @config
end

Class Method Details

.capabilitiesHash?

Returns capabilities.

Returns:

  • (Hash, nil)

    capabilities



168
169
170
# File 'lib/ask/provider.rb', line 168

def capabilities
  nil
end

.clear_providers!Object

Clear all registered providers (used in testing).



149
150
151
152
153
# File 'lib/ask/provider.rb', line 149

def clear_providers!
  REGISTRY_MUTEX.synchronize do
    @registry = {}
  end
end

.configuration_optionsArray<Symbol>

Returns config keys this provider supports.

Returns:

  • (Array<Symbol>)

    config keys this provider supports



173
174
175
# File 'lib/ask/provider.rb', line 173

def configuration_options
  []
end

.configuration_requirementsArray<Symbol>

Returns config keys this provider requires.

Returns:

  • (Array<Symbol>)

    config keys this provider requires



178
179
180
# File 'lib/ask/provider.rb', line 178

def configuration_requirements
  []
end

.configured?(config) ⇒ Boolean

Check if this provider is fully configured.

Parameters:

  • config (Object)

    configuration object

Returns:

  • (Boolean)


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

def configured?(config)
  configuration_requirements.all? { |req| config.respond_to?(req) && config.public_send(req) }
end

.local?Boolean

Returns true if this provider runs locally.

Returns:

  • (Boolean)

    true if this provider runs locally



190
191
192
# File 'lib/ask/provider.rb', line 190

def local?
  false
end

.nameString

Returns class name without module prefix.

Returns:

  • (String)

    class name without module prefix



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

def name
  to_s.split("::").last
end

.providersHash{Symbol => Class<Ask::Provider>}

Return a shallow copy of all registered providers. Thread-safe via REGISTRY_MUTEX.

Returns:



142
143
144
145
146
# File 'lib/ask/provider.rb', line 142

def providers
  REGISTRY_MUTEX.synchronize do
    registry.dup
  end
end

.register(name, provider_class) ⇒ Object

Register a provider class so it can be resolved by name. Thread-safe via REGISTRY_MUTEX.

Parameters:

  • name (Symbol)

    short name for the provider

  • provider_class (Class<Ask::Provider>)

    the provider class



120
121
122
123
124
# File 'lib/ask/provider.rb', line 120

def register(name, provider_class)
  REGISTRY_MUTEX.synchronize do
    registry[name.to_sym] = provider_class
  end
end

.remote?Boolean

Returns true if this provider requires a remote API.

Returns:

  • (Boolean)

    true if this provider requires a remote API



195
196
197
# File 'lib/ask/provider.rb', line 195

def remote?
  !local?
end

.resolve(name) ⇒ Class<Ask::Provider>

Resolve a registered provider by name. Thread-safe via REGISTRY_MUTEX.

Parameters:

  • name (Symbol, String)

    provider name

Returns:

Raises:



131
132
133
134
135
136
137
# File 'lib/ask/provider.rb', line 131

def resolve(name)
  REGISTRY_MUTEX.synchronize do
    registry[name.to_sym] || raise(UnknownProvider,
                                    "Unknown provider: #{name.inspect}. " \
                                    "Available: #{registry.keys.join(', ')}")
  end
end

.slugString

Returns lowercased, underscored slug from the class name.

Returns:

  • (String)

    lowercased, underscored slug from the class name



156
157
158
159
160
# File 'lib/ask/provider.rb', line 156

def slug
  name.split("::").last.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
      .gsub(/([a-z\d])([A-Z])/, '\1_\2')
      .downcase
end

Instance Method Details

#api_baseString

This method is abstract.

The base URL for this provider's API.

Returns:

  • (String)

Raises:

  • (NotImplementedError)


71
72
73
# File 'lib/ask/provider.rb', line 71

def api_base
  raise NotImplementedError, "#{self.class} must implement #api_base"
end

#capabilitiesHash?

Returns provider capabilities metadata.

Returns:

  • (Hash, nil)

    provider capabilities metadata



109
110
111
# File 'lib/ask/provider.rb', line 109

def capabilities
  self.class.capabilities
end

#chat(messages, model:, tools: nil, temperature: nil, stream: nil, schema: nil, **params) {|Ask::Chunk| ... } ⇒ Ask::Message

Send a chat completion request.

Parameters:

  • messages (Array<Ask::Message>)

    conversation messages

  • model (String)

    model ID to use

  • tools (Array<Ask::ToolDef>, nil) (defaults to: nil)

    tool definitions

  • temperature (Float, nil) (defaults to: nil)

    sampling temperature

  • stream (Boolean, nil) (defaults to: nil)

    if true, yield Chunks to the block

  • schema (Hash, nil) (defaults to: nil)

    JSON schema for structured output

Yields:

Returns:

Raises:

  • (NotImplementedError)


51
52
53
# File 'lib/ask/provider.rb', line 51

def chat(messages, model:, tools: nil, temperature: nil, stream: nil, schema: nil, **params, &block)
  raise NotImplementedError, "#{self.class} must implement #chat"
end

#embed(text, model:) ⇒ Array<Float>

Generate embeddings for the given text.

Parameters:

  • text (String)

    input text

  • model (String)

    embedding model ID

Returns:

  • (Array<Float>)

    embedding vector

Raises:

  • (NotImplementedError)


59
60
61
# File 'lib/ask/provider.rb', line 59

def embed(text, model:)
  raise NotImplementedError, "#{self.class} must implement #embed"
end

#headersHash<String, String>

Additional HTTP headers for API requests.

Returns:

  • (Hash<String, String>)


79
80
81
# File 'lib/ask/provider.rb', line 79

def headers
  {}
end

#list_modelsArray<Ask::ModelInfo>

List available models from this provider.

Returns:

Raises:

  • (NotImplementedError)


65
66
67
# File 'lib/ask/provider.rb', line 65

def list_models
  raise NotImplementedError, "#{self.class} must implement #list_models"
end

#local?Boolean

Returns true if the provider runs locally (e.g., Ollama).

Returns:

  • (Boolean)

    true if the provider runs locally (e.g., Ollama)



91
# File 'lib/ask/provider.rb', line 91

def local? = self.class.local?

#nameString

Returns provider name (demodulized class name).

Returns:

  • (String)

    provider name (demodulized class name)



104
105
106
# File 'lib/ask/provider.rb', line 104

def name
  self.class.name
end

#parse_error(response) ⇒ String?

Parse an error response body into a human-readable message.

Parameters:

  • response (Object)

    the error response

Returns:

  • (String, nil)


86
87
88
# File 'lib/ask/provider.rb', line 86

def parse_error(response)
  nil
end

#remote?Boolean

Returns true if the provider requires a remote API.

Returns:

  • (Boolean)

    true if the provider requires a remote API



94
# File 'lib/ask/provider.rb', line 94

def remote? = !local?

#slugString

Returns lowercased provider slug.

Returns:

  • (String)

    lowercased provider slug



99
100
101
# File 'lib/ask/provider.rb', line 99

def slug
  self.class.slug
end