Class: Prescient::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/prescient/client.rb

Overview

Client class for interacting with AI providers

The Client provides a high-level interface for working with AI providers, handling error recovery, retries, and method delegation. It acts as a facade over the configured providers.

Examples:

Basic usage

client = Prescient::Client.new(:openai)
response = client.generate_response("Hello, world!")
embedding = client.generate_embedding("Text to embed")

Using default provider

client = Prescient::Client.new  # Uses configured default
puts client.provider_name       # => :ollama (or configured default)

Author:

  • Claude Code

Since:

  • 1.0.0

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(provider_name = nil, enable_fallback: true) ⇒ Client

Initialize a new client with the specified provider

Parameters:

  • provider_name (Symbol, nil) (defaults to: nil)

    Name of provider to use, or nil for default

  • enable_fallback (Boolean) (defaults to: true)

    Whether to enable automatic fallback to other providers

Raises:

Since:

  • 1.0.0



33
34
35
36
37
38
39
# File 'lib/prescient/client.rb', line 33

def initialize(provider_name = nil, enable_fallback: true)
  @provider_name = provider_name || Prescient.configuration.default_provider
  @provider = Prescient.configuration.provider(@provider_name)
  @enable_fallback = enable_fallback

  raise Prescient::Error, "Provider not found: #{@provider_name}" unless @provider
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name) ⇒ Object

Since:

  • 1.0.0



114
115
116
# File 'lib/prescient/client.rb', line 114

def method_missing(method_name, ...)
  @provider.respond_to?(method_name) ? @provider.send(method_name, ...) : super
end

Instance Attribute Details

#providerBase (readonly)

Returns The underlying provider instance.

Returns:

  • (Base)

    The underlying provider instance

Since:

  • 1.0.0



26
27
28
# File 'lib/prescient/client.rb', line 26

def provider
  @provider
end

#provider_nameSymbol (readonly)

Returns The name of the provider being used.

Returns:

  • (Symbol)

    The name of the provider being used

Since:

  • 1.0.0



23
24
25
# File 'lib/prescient/client.rb', line 23

def provider_name
  @provider_name
end

Instance Method Details

#available?Boolean

Check if the provider is currently available

Returns:

  • (Boolean)

    true if provider is healthy and available

Since:

  • 1.0.0



95
96
97
# File 'lib/prescient/client.rb', line 95

def available?
  @provider.available?
end

#generate_embedding(text, **options) ⇒ Array<Float>

Generate embeddings for the given text

Delegates to the underlying provider with automatic retry logic for transient failures. If fallback is enabled, tries other providers on persistent failures.

Parameters:

  • text (String)

    The text to generate embeddings for

  • options (Hash)

    Provider-specific options

Returns:

  • (Array<Float>)

    Array of embedding values

Raises:

Since:

  • 1.0.0



51
52
53
54
55
56
57
58
59
# File 'lib/prescient/client.rb', line 51

def generate_embedding(text, **options)
  if @enable_fallback
    with_fallback_handling(:generate_embedding, text, **options)
  else
    with_error_handling do
      @provider.generate_embedding(text, **options)
    end
  end
end

#generate_response(prompt, context_items = [], **options) ⇒ Hash

Generate text response for the given prompt

Delegates to the underlying provider with automatic retry logic for transient failures. Supports optional context items for RAG. If fallback is enabled, tries other providers on persistent failures.

Parameters:

  • prompt (String)

    The prompt to generate a response for

  • context_items (Array<Hash, String>) (defaults to: [])

    Optional context items

  • options (Hash)

    Provider-specific generation options

Options Hash (**options):

  • :temperature (Float)

    Sampling temperature (0.0-2.0)

  • :max_tokens (Integer)

    Maximum tokens to generate

  • :top_p (Float)

    Nucleus sampling parameter

Returns:

  • (Hash)

    Response hash with :response, :model, :provider keys

Raises:

Since:

  • 1.0.0



75
76
77
78
79
80
81
82
83
# File 'lib/prescient/client.rb', line 75

def generate_response(prompt, context_items = [], **options)
  if @enable_fallback
    with_fallback_handling(:generate_response, prompt, context_items, **options)
  else
    with_error_handling do
      @provider.generate_response(prompt, context_items, **options)
    end
  end
end

#health_checkHash

Check the health status of the provider

Returns:

  • (Hash)

    Health status information

Since:

  • 1.0.0



88
89
90
# File 'lib/prescient/client.rb', line 88

def health_check
  @provider.health_check
end

#provider_infoHash

Get comprehensive information about the provider

Returns details about the provider including its availability and configuration options (with sensitive data removed).

Returns:

  • (Hash)

    Provider information including :name, :class, :available, :options

Since:

  • 1.0.0



105
106
107
108
109
110
111
112
# File 'lib/prescient/client.rb', line 105

def provider_info
  {
    name:      @provider_name,
    class:     @provider.class.name.split('::').last,
    available: available?,
    options:   sanitize_options(@provider.options),
  }
end

#providers_to_tryObject (private)

Since:

  • 1.0.0



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/prescient/client.rb', line 178

def providers_to_try
  providers = [@provider_name]

  # Add configured fallback providers
  fallback_providers = Prescient.configuration.fallback_providers
  if fallback_providers && !fallback_providers.empty?
    providers += fallback_providers.reject { |p| p == @provider_name }
  else
    # If no explicit fallbacks configured, try all available providers
    available = Prescient.configuration.available_providers
    providers += available.reject { |p| p == @provider_name }
  end

  providers.uniq
end

#respond_to_missing?(method_name, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)

Since:

  • 1.0.0



118
119
120
# File 'lib/prescient/client.rb', line 118

def respond_to_missing?(method_name, include_private = false)
  @provider.respond_to?(method_name, include_private) || super
end

#sanitize_options(options) ⇒ Object (private)

TODO: configurable keys to sanitize

Since:

  • 1.0.0



125
126
127
128
# File 'lib/prescient/client.rb', line 125

def sanitize_options(options)
  sensitive_keys = [:api_key, :password, :token, :secret]
  options.reject { |key, _| sensitive_keys.include?(key.to_sym) }
end

#with_error_handlingObject (private)

Since:

  • 1.0.0



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/prescient/client.rb', line 130

def with_error_handling
  retries = 0
  begin
    yield
  rescue Prescient::RateLimitError => e
    raise e unless retries < Prescient.configuration.retry_attempts

    retries += 1
    sleep(Prescient.configuration.retry_delay * retries)
    retry
  rescue Prescient::ConnectionError => e
    raise e unless retries < Prescient.configuration.retry_attempts

    retries += 1
    sleep(Prescient.configuration.retry_delay)
    retry
  end
end

#with_fallback_handling(method_name, *args, **options) ⇒ Object (private)

Since:

  • 1.0.0



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/prescient/client.rb', line 149

def with_fallback_handling(method_name, *args, **options)
  last_error = nil

  providers_to_try.each_with_index do |provider_name, index|
    # Use existing provider instance for primary provider, create new ones for fallbacks
    provider = if index.zero? && provider_name == @provider_name
                 @provider
               else
                 Prescient.configuration.provider(provider_name)
               end
    next unless provider

    # Check if provider is available before trying
    next unless provider.available?

    # Use retry logic for each provider
    return with_error_handling do
      provider.send(method_name, *args, **options)
    end
  rescue Prescient::Error => e
    last_error = e
    # Log the error and continue to next provider
    next
  end

  # If we get here, all providers failed
  raise last_error || Prescient::Error.new("No available providers for #{method_name}")
end