Class: AiClient

Inherits:
Object
  • Object
show all
Defined in:
lib/ai_client.rb,
lib/ai_client/llm.rb,
lib/ai_client/chat.rb,
lib/ai_client/embed.rb,
lib/ai_client/speak.rb,
lib/ai_client/version.rb,
lib/ai_client/function.rb,
lib/ai_client/middleware.rb,
lib/ai_client/transcribe.rb,
lib/ai_client/configuration.rb,
lib/ai_client/retry_middleware.rb,
lib/ai_client/logger_middleware.rb,
lib/ai_client/open_router_extensions.rb

Overview

ai_client/logger_middleware.rb

Defined Under Namespace

Modules: HashRefinement Classes: Config, Function, LLM, LoggingMiddleware, RetryMiddleware, Tool

Constant Summary collapse

VERSION =
"0.4.1"

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(model = nil, **options) {|config| ... } ⇒ AiClient

Initializes a new AiClient instance.

You can over-ride the class config by providing a block like this

c = AiClient.new(...) do |config|
      config.logger = nil
    end

You can also load an instance’s config from a YAML file.

c = AiClient.new('model_name'. cpmfog: 'path/to/file.yml', ...)

… and you can do both = load from a file and

over-ride with a config block

The options object is basically those things that the OmniAI clients want to see.

Parameters:

  • model (String) (defaults to: nil)

    The model name to use for the client.

  • options (Hash)

    Optional named parameters:

    • :provider [Symbol] Specify the provider.

    • :config [String] Path to a YAML configuration file.

    • :logger [Logger] Logger instance for the client.

    • :timeout [Integer] Timeout value for requests.

Yields:

  • (config)

    An optional block to configure the instance.



119
120
121
122
123
124
125
126
127
128
129
# File 'lib/ai_client.rb', line 119

def initialize(model = nil, **options, &block)
  @context        = [] # An Array of String or response objects
  @last_messages  = nil
  @last_response  = nil

  setup_config(options, &block)
  set_provider_and_model(model, options[:provider])
  setup_instance_variables(options)

  @client = create_client
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args, &block) ⇒ Object

Handles calls to methods that are missing on the AiClient instance.

Parameters:

  • method_name (Symbol)

    The name of the method called.

  • args (Array)

    Arguments passed to the method.

  • block (Proc)

    Optional block associated with the method call.

Returns:

  • (Object)

    The result from the underlying client or raises NoMethodError.



181
182
183
184
185
186
187
188
189
# File 'lib/ai_client.rb', line 181

def method_missing(method_name, *args, &block)
  if @client.respond_to?(method_name)
    result = @client.send(method_name, *args, &block)
    @last_response = result if result.is_a?(OmniAI::Response)
    result
  else
    super
  end
end

Class Attribute Details

.class_configObject

Returns the value of attribute class_config.



127
128
129
# File 'lib/ai_client/configuration.rb', line 127

def class_config
  @class_config
end

.default_configObject

Returns the value of attribute default_config.



127
128
129
# File 'lib/ai_client/configuration.rb', line 127

def default_config
  @default_config
end

Instance Attribute Details

#clientObject (readonly)

OmniAI’s client instance



85
86
87
# File 'lib/ai_client.rb', line 85

def client
  @client
end

#configObject (readonly)

OmniAI’s client instance



85
86
87
# File 'lib/ai_client.rb', line 85

def config
  @config
end

#contextObject (readonly)

OmniAI’s client instance



85
86
87
# File 'lib/ai_client.rb', line 85

def context
  @context
end

#last_messageObject (readonly)

OmniAI’s client instance



85
86
87
# File 'lib/ai_client.rb', line 85

def last_message
  @last_message
end

#last_responseObject (readonly)

OmniAI’s client instance



85
86
87
# File 'lib/ai_client.rb', line 85

def last_response
  @last_response
end

#loggerObject (readonly)

OmniAI’s client instance



85
86
87
# File 'lib/ai_client.rb', line 85

def logger
  @logger
end

#modelObject (readonly)

OmniAI’s client instance



85
86
87
# File 'lib/ai_client.rb', line 85

def model
  @model
end

#providerObject (readonly)

OmniAI’s client instance



85
86
87
# File 'lib/ai_client.rb', line 85

def provider
  @provider
end

#timeoutObject (readonly)

OmniAI’s client instance



85
86
87
# File 'lib/ai_client.rb', line 85

def timeout
  @timeout
end

Class Method Details

.add_open_router_extensionsvoid

This method returns an undefined value.

Initializes OpenRouter extensions for AiClient.

This sets up the access token and initializes the ORC client.



74
75
76
77
78
79
80
81
# File 'lib/ai_client/open_router_extensions.rb', line 74

def add_open_router_extensions
  access_token = fetch_access_token

  return unless access_token

  configure_open_router(access_token)
  initialize_orc_client
end

.clear_middlewaresvoid

This method returns an undefined value.

Clears all middlewares from the client.



56
57
58
# File 'lib/ai_client/middleware.rb', line 56

def clear_middlewares
  @middlewares = []
end

.configure {|config| ... } ⇒ void

This method returns an undefined value.

Configures the AiClient with a given block.

Yield Parameters:



134
135
136
# File 'lib/ai_client/configuration.rb', line 134

def configure(&block)
  yield(class_config)
end

.middlewaresArray

Returns the list of middlewares applied to the client.

Returns:

  • (Array)

    list of middlewares



38
39
40
# File 'lib/ai_client/middleware.rb', line 38

def middlewares
  @middlewares ||= []
end

.model_details(model_id) ⇒ AiClient::LLM?

Retrieves details for a specific model.

Parameters:

  • model_id (String)

    The model ID to retrieve details for, in the pattern “provider/model”.downcase

Returns:

  • (AiClient::LLM, nil)

    Details of the model or nil if not found.



58
# File 'lib/ai_client/open_router_extensions.rb', line 58

def model_details(model_id) = LLM.find(model_id.downcase)

.models(substring = nil) ⇒ Array<String>

Retrieves model names, optionally filtered by provider.

Parameters:

  • substring (String, nil) (defaults to: nil)

    Optional substring to filter models by.

Returns:

  • (Array<String>)

    List of model names.



51
# File 'lib/ai_client/open_router_extensions.rb', line 51

def models(substring = nil) = LLM.models(substring)

.orc_clientOpenRouter::Client

Retrieves the ORC client instance.

Returns:

  • (OpenRouter::Client)

    Instance of the OpenRouter client.



89
90
91
# File 'lib/ai_client/open_router_extensions.rb', line 89

def orc_client
  @orc_client ||= add_open_router_extensions || raise("OpenRouter extensions are not available")
end

.providersArray<Symbol>

Retrieves all available providers.

Returns:

  • (Array<Symbol>)

    List of all provider names.



44
# File 'lib/ai_client/open_router_extensions.rb', line 44

def providers = LLM.providers

.reset_default_configvoid

This method returns an undefined value.

Resets the default configuration to the value defined in the class.



142
143
144
145
# File 'lib/ai_client/configuration.rb', line 142

def reset_default_config
  initialize_defaults
    .save(Config::DEFAULT_CONFIG_FILEPATH)      
end

.reset_llm_datavoid

This method returns an undefined value.

Resets LLM data with the available ORC models.



42
43
44
45
46
# File 'lib/ai_client/llm.rb', line 42

def reset_llm_data
  orc_models = AiClient.orc_client.models
  AiClient::LLM.data = orc_models
  AiClient::LLM::DATA_PATH.write(orc_models.to_yaml)
end

.use(middleware) ⇒ void

This method returns an undefined value.

Adds a middleware to the stack.

Parameters:

  • middleware (Proc)

    the middleware to be added



48
49
50
# File 'lib/ai_client/middleware.rb', line 48

def use(middleware)
  middlewares << middleware
end

.versionObject



7
# File 'lib/ai_client/version.rb', line 7

def self.version  = VERSION

Instance Method Details

#add_context(my_prompt) ⇒ String+

Adds context to the current prompt.

Parameters:

  • prompt (String, Array<String>)

    the current prompt.

Returns:

  • (String, Array<String>)

    the prompt with context added.



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/ai_client/chat.rb', line 60

def add_context(my_prompt)
  return(my_prompt)   if  @config.context_length.nil? || 
                          0 == @config.context_length ||
                          my_prompt.is_a?(Array)      || 
                          @context.empty?

  prompt = "\nUser: #{my_prompt} Bot: "
  prompt.prepend(
    @context.map{|entry|
      "User: #{entry[:user]} Bot: #{entry[:bot]}"
    }.join("\n")
  )

  prompt
end

#batch_embed(inputs, batch_size: 100, **params) ⇒ Object



14
15
16
17
18
19
# File 'lib/ai_client/embed.rb', line 14

def batch_embed(inputs, batch_size: 100, **params)
  inputs.each_slice(batch_size).flat_map do |batch|
    sleep 1 # DEBUG rate limits being exceeded
    embed(batch, **params)
  end
end

#call_with_middlewares(method, *args, **kwargs, &block) ⇒ Object

Calls the specified method with middlewares applied.

Parameters:

  • method (Symbol)

    the name of the method to be called

  • args (Array)

    additional arguments for the method

  • kwargs (Hash)

    named parameters for the method

  • block (Proc)

    optional block to be passed to the method

Returns:

  • (Object)

    result of the method call after applying middlewares



24
25
26
27
28
29
# File 'lib/ai_client/middleware.rb', line 24

def call_with_middlewares(method, *args, **kwargs, &block)
  stack = self.class.middlewares.reverse.reduce(-> { send(method, *args, **kwargs, &block) }) do |next_middleware, middleware|
    -> { middleware.call(self, next_middleware, *args, **kwargs, &block) }
  end
  stack.call
end

#chat(messages = '', **params, &block) ⇒ String

OmniAI Params

model:        @model    [String] optional
format:       @format   [Symbol] optional :text or :json
stream:       @stream   [Proc, nil] optional
tools:        @tools    [Array<OmniAI::Tool>] optional
temperature:  @temperature  [Float, nil] optional

Initiates a chat session.

Parameters:

  • messages (Array<String>) (defaults to: '')

    the messages to send.

  • params (Hash)

    optional parameters.

Options Hash (**params):

  • :tools (Array<OmniAI::Tool>)

    an array of tools to use.

Returns:

  • (String)

    the result from the chat.

Raises:

  • (RuntimeError)

    if tools parameter is invalid.



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/ai_client/chat.rb', line 22

def chat(messages='', **params, &block)    
  if params.has_key? :tools
    tools = params[:tools]
    if tools.is_a? Array
      tools.map!{|function_name| AiClient::Function.registry[function_name]}
    elsif true == tools
      tools = AiClient::Function.registry.values
    else
      raise 'what is this'
    end
    params[:tools] = tools
  end

  @last_messages  = messages
  messages        = add_context(messages)
  result          = call_with_middlewares(
                      :chat_without_middlewares, 
                      messages, 
                      **params,
                      &block
                    )
  @last_response  = result
  result          = raw? ? result : content

  @context  = @context.push({
                user: @last_messages,
                bot:  result
              }).last(config.context_length)

  result
end

#chat_without_middlewares(messages, **params, &block) ⇒ String

Chats with the client without middleware processing.

Parameters:

  • messages (Array<String>)

    the messages to send.

  • params (Hash)

    optional parameters.

Returns:

  • (String)

    the result from the chat.



92
93
94
# File 'lib/ai_client/chat.rb', line 92

def chat_without_middlewares(messages, **params, &block)
  @client.chat(messages, model: @model, **params, &block)
end

#chatbotObject



97
98
99
100
101
102
103
104
105
106
107
# File 'lib/ai_client/chat.rb', line 97

def chatbot
  prompt = "hello"
  until prompt.empty? do
    response = chat prompt
    puts
    puts content
    puts
    print "Follow Up: "
    prompt = gets.chomp
  end
end

#clear_contextvoid

This method returns an undefined value.

Clears the current context.



81
82
83
# File 'lib/ai_client/chat.rb', line 81

def clear_context
  @context = []
end

#content(response = last_response) ⇒ String Also known as: text

Extracts the content from the last response based on the provider.

Returns:

  • (String)

    The extracted content.

Raises:

  • (NotImplementedError)

    If content extraction is not implemented for the provider.



160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/ai_client.rb', line 160

def content(response=last_response)
  case @provider
  when :localai, :mistral, :ollama, :open_router, :openai
    response.data.tunnel 'content'
    
  when :anthropic, :google
    response.data.tunnel 'text'

  else
    raise NotImplementedError, "Content extraction not implemented for provider: #{@provider}"
  end
end

#embed(input, **params) ⇒ Object

OmniAI Params

model [String] required


10
11
12
# File 'lib/ai_client/embed.rb', line 10

def embed(input, **params)
  @client.embed(input, model: @model, **params)
end

#model_detailsHash?

Retrieves details for the current model.

Returns:

  • (Hash, nil)

    Details of the current model or nil if not found.



28
29
30
31
# File 'lib/ai_client/open_router_extensions.rb', line 28

def model_details
  id = "#{@provider}/#{@model}"
  LLM.find(id.downcase)
end

#modelsArray<String>

Retrieves model names for the current provider.

Returns:

  • (Array<String>)

    List of model names for the current provider.



36
# File 'lib/ai_client/open_router_extensions.rb', line 36

def models = LLM.models(@provider)

#raw=(value) ⇒ Object

Sets whether to return raw responses.

Parameters:

  • value (Boolean)

    The value to set for raw responses return.



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

def raw=(value)
  config.return_raw = value
end

#raw?Boolean

Checks if the client is set to return raw responses.

Returns:

  • (Boolean)

    True if raw responses are to be returned.



135
136
137
# File 'lib/ai_client.rb', line 135

def raw?
  config.return_raw
end

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

Checks if the instance responds to the missing method.

Parameters:

  • method_name (Symbol)

    The name of the method to check.

  • include_private (Boolean) (defaults to: false)

    Whether to include private methods in the check.

Returns:

  • (Boolean)

    True if the method is supported by the client, false otherwise.



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

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

#responseOmniAI::Response

Returns the last response received from the client.

Returns:

  • (OmniAI::Response)

    The last response.



152
# File 'lib/ai_client.rb', line 152

def response  = last_response

#speak(text, **params) {|output| ... } ⇒ Tempfile``

OmniAI Params

input   [String] required
model   [String] required
voice   [String] required
speed   [Float] optional
format  [String] optional (default "aac")
  aac mp3 flac opus pcm wav

Yields:

  • (output)

    optional

Returns:

  • (Tempfile``)


18
19
20
# File 'lib/ai_client/speak.rb', line 18

def speak(text, **params)
  call_with_middlewares(:speak_without_middlewares, text, **params)
end

#speak_without_middlewares(text, **params) ⇒ Object



22
23
24
# File 'lib/ai_client/speak.rb', line 22

def speak_without_middlewares(text, **params)
  @client.speak(text, model: @model, **params)
end

#transcribe(audio, format: nil, **params) ⇒ Object

OmniAI Params

model    [String]
language [String, nil] optional
prompt   [String, nil] optional
format   [Symbol] :text, :srt, :vtt, or :json (default)
temperature [Float, nil] optional


13
14
15
# File 'lib/ai_client/transcribe.rb', line 13

def transcribe(audio, format: nil, **params)
  call_with_middlewares(:transcribe_without_middlewares, audio, format: format, **params)
end

#transcribe_without_middlewares(audio, format: nil, **params) ⇒ Object



17
18
19
# File 'lib/ai_client/transcribe.rb', line 17

def transcribe_without_middlewares(audio, format: nil, **params)
  @client.transcribe(audio, model: @model, format: format, **params)
end

#versionObject



6
# File 'lib/ai_client/version.rb', line 6

def version       = VERSION