ai_client 0.2.3 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,16 +5,56 @@
5
5
  # a valid API Key for the open_router.ai web-service
6
6
 
7
7
  require 'open_router'
8
+ require 'yaml'
8
9
 
9
10
  class AiClient
11
+
12
+ # Retrieves the available models.
13
+ #
14
+ # @return [Array<String>] List of model IDs.
15
+ #
16
+ def models
17
+ self.class.models
18
+ end
19
+
20
+ # Retrieves the available providers.
21
+ #
22
+ # @return [Array<String>] List of provider names.
23
+ def providers
24
+ self.class.providers
25
+ end
26
+
27
+ # Retrieves model names, optionally filtered by provider.
28
+ #
29
+ # @param provider [String, nil] The provider to filter models by.
30
+ # @return [Array<String>] List of model names.
31
+ def model_names(provider = nil)
32
+ self.class.model_names(provider)
33
+ end
34
+
35
+ # Retrieves details for a specific model.
36
+ #
37
+ # @param a_model [String] The model ID to retrieve details for.
38
+ # @return [Hash, nil] Details of the model or nil if not found.
39
+ def model_details(a_model)
40
+ self.class.model_details(a_model)
41
+ end
42
+
43
+ # Finds models matching a given substring.
44
+ #
45
+ # @param a_model_substring [String] The substring to search for.
46
+ # @return [Array<String>] List of matching model names.
47
+ def find_model(a_model_substring)
48
+ self.class.find_model(a_model_substring)
49
+ end
10
50
 
11
- def models = self.class.models
12
- def providers = self.class.providers
13
- def model_names(a_provider=nil) = self.class.model_names(a_provider)
14
- def model_details(a_model) = self.class.model_details(a_model)
15
- def find_model(a_model_substring) = self.class.find_model(a_model_substring)
16
51
 
17
52
  class << self
53
+
54
+ # Adds OpenRouter extensions to AiClient.
55
+ #
56
+ # @return [void]
57
+ #
18
58
  def add_open_router_extensions
19
59
  access_token = fetch_access_token
20
60
 
@@ -24,17 +64,30 @@ class AiClient
24
64
  initialize_orc_client
25
65
  end
26
66
 
67
+ # Retrieves ORC client instance.
68
+ #
69
+ # @return [OpenRouter::Client] Instance of the OpenRouter client.
70
+ #
27
71
  def orc_client
28
72
  @orc_client ||= add_open_router_extensions || raise("OpenRouter extensions are not available")
29
73
  end
30
74
 
31
- def models
32
- @models ||= orc_client.models
75
+ # Retrieves models from the ORC client.
76
+ #
77
+ # @return [Array<Hash>] List of models.
78
+ #
79
+ def orc_models
80
+ @orc_models ||= orc_client.models
33
81
  end
34
82
 
35
83
  # TODO: Refactor these DB like methods to take
36
84
  # advantage of AiClient::LLM
37
85
 
86
+ # Retrieves model names associated with a provider.
87
+ #
88
+ # @param provider [String, nil] The provider to filter models by.
89
+ # @return [Array<String>] List of model names.
90
+ #
38
91
  def model_names(provider=nil)
39
92
  model_ids = models.map { _1['id'] }
40
93
 
@@ -43,37 +96,72 @@ class AiClient
43
96
  model_ids.filter_map { _1.split('/')[1] if _1.start_with?(provider.to_s.downcase) }
44
97
  end
45
98
 
99
+ # Retrieves details of a specific model.
100
+ #
101
+ # @param model [String] The model ID to retrieve details for.
102
+ # @return [Hash, nil] Details of the model or nil if not found.
103
+ #
46
104
  def model_details(model)
47
105
  orc_models.find { _1['id'].include?(model) }
48
106
  end
49
107
 
108
+ # Retrieves the available providers.
109
+ #
110
+ # @return [Array<String>] List of unique provider names.
111
+ #
50
112
  def providers
51
113
  @providers ||= models.map{ _1['id'].split('/')[0] }.sort.uniq
52
114
  end
53
115
 
116
+ # Finds models matching a given substring.
117
+ #
118
+ # @param a_model_substring [String] The substring to search for.
119
+ # @return [Array<String>] List of matching model names.
120
+ #
54
121
  def find_model(a_model_substring)
55
122
  model_names.select{ _1.include?(a_model_substring) }
56
123
  end
57
124
 
125
+ # Resets LLM data with the available ORC models.
126
+ #
127
+ # @return [void]
128
+ #
129
+ def reset_llm_data
130
+ LLM.data = orc_models
131
+ LLM::DATA_PATH.write(orc_models.to_yaml)
132
+ end
133
+
134
+
58
135
  private
59
136
 
60
- # Similar to fetch_api_key but for the class_config
137
+ # Fetches the access token from environment variables.
138
+ #
139
+ # @return [String, nil] The access token or nil if not found.
140
+ #
61
141
  def fetch_access_token
62
142
  class_config.envar_api_key_names.open_router
63
143
  .map { |key| ENV[key] }
64
144
  .compact
65
145
  .first
66
146
  end
67
-
147
+
148
+ # Configures the OpenRouter client with the access token.
149
+ #
150
+ # @param access_token [String] The access token to configure.
151
+ # @return [void]
152
+ #
68
153
  def configure_open_router(access_token)
69
154
  OpenRouter.configure { |config| config.access_token = access_token }
70
155
  end
71
156
 
157
+ # Initializes the ORC client.
158
+ #
159
+ # @return [void]
160
+ #
72
161
  def initialize_orc_client
73
162
  @orc_client ||= OpenRouter::Client.new
74
163
  end
75
164
  end
76
165
  end
77
166
 
78
-
79
167
  AiClient.add_open_router_extensions
@@ -11,12 +11,27 @@ class AiClient
11
11
  # )
12
12
  #
13
13
  class RetryMiddleware
14
+
15
+ # Initializes a new instance of RetryMiddleware.
16
+ #
17
+ # @param max_retries [Integer] The maximum number of retries to attempt (default: 3).
18
+ # @param base_delay [Integer] The base delay in seconds before retrying (default: 2).
19
+ # @param max_delay [Integer] The maximum delay in seconds between retries (default: 16).
20
+ #
14
21
  def initialize(max_retries: 3, base_delay: 2, max_delay: 16)
15
22
  @max_retries = max_retries
16
23
  @base_delay = base_delay
17
24
  @max_delay = max_delay
18
25
  end
19
26
 
27
+ # Calls the next middleware, retrying on specific errors.
28
+ #
29
+ # @param client [AiClient] The client instance that invokes the middleware.
30
+ # @param next_middleware [Proc] The next middleware in the chain to call.
31
+ # @param args [Array] Any additional arguments to pass to the next middleware.
32
+ #
33
+ # @raise [StandardError] Reraise the error if max retries are exceeded.
34
+ #
20
35
  def call(client, next_middleware, *args)
21
36
  retries = 0
22
37
  begin
@@ -0,0 +1,18 @@
1
+ # lib/ai_client/tool.rb
2
+
3
+ # TODO: Turn this into a Function class using the pattern
4
+ # in examples/tools.rb
5
+ # put the function names as symbols into a class Array
6
+ # In the AiClient class transform the tools: []
7
+ # parameter from an Array of Symbols into an Array
8
+ # of FUnction instances.
9
+
10
+ class AiClient::Tool < OmniAI::Tool
11
+
12
+ def xyzzy = self.class.xyzzy
13
+
14
+ class << self
15
+ def xyzzy = puts "Magic"
16
+ end
17
+ end
18
+
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class AiClient
4
- VERSION = "0.2.3"
4
+ VERSION = "0.2.5"
5
5
  end
data/lib/ai_client.rb CHANGED
@@ -16,6 +16,8 @@ require 'omniai/openai'
16
16
 
17
17
  require 'open_router'
18
18
 
19
+ require_relative 'ai_client/version'
20
+
19
21
  require_relative 'ai_client/chat'
20
22
  require_relative 'ai_client/embed'
21
23
  require_relative 'ai_client/speak'
@@ -23,10 +25,10 @@ require_relative 'ai_client/transcribe'
23
25
 
24
26
  require_relative 'ai_client/configuration'
25
27
  require_relative 'ai_client/middleware'
26
- require_relative 'ai_client/version'
27
28
 
28
29
  require_relative 'ai_client/open_router_extensions'
29
30
  require_relative 'ai_client/llm' # SMELL: must come after the open router stuff
31
+ require_relative 'ai_client/tool'
30
32
 
31
33
  # Create a generic client instance using only model name
32
34
  # client = AiClient.new('gpt-3.5-turbo')
@@ -76,6 +78,8 @@ class AiClient
76
78
  :timeout,
77
79
  :config # Instance configuration
78
80
 
81
+ # Initializes a new AiClient instance.
82
+ #
79
83
  # You can over-ride the class config by providing a block like this
80
84
  # c = AiClient.new(...) do |config|
81
85
  # config.logger = nil
@@ -90,6 +94,14 @@ class AiClient
90
94
  # The options object is basically those things that the
91
95
  # OmniAI clients want to see.
92
96
  #
97
+ # @param model [String] The model name to use for the client.
98
+ # @param options [Hash] Optional named parameters:
99
+ # - :provider [Symbol] Specify the provider.
100
+ # - :config [String] Path to a YAML configuration file.
101
+ # - :logger [Logger] Logger instance for the client.
102
+ # - :timeout [Integer] Timeout value for requests.
103
+ # @yield [config] An optional block to configure the instance.
104
+ #
93
105
  def initialize(model, **options, &block)
94
106
  # Assign the instance variable @config from the class variable @@config
95
107
  @config = self.class.class_config.dup
@@ -121,13 +133,34 @@ class AiClient
121
133
  @last_response = nil
122
134
  end
123
135
 
136
+ # TODO: Review these raw-ish methods are they really needed?
137
+ # raw? should be a private method ??
138
+
139
+ # Returns the last response received from the client.
140
+ #
141
+ # @return [OmniAI::Response] The last response.
142
+ #
124
143
  def response = last_response
144
+
145
+ # Checks if the client is set to return raw responses.
146
+ #
147
+ # @return [Boolean] True if raw responses are to be returned.
125
148
  def raw? = config.return_raw
126
149
 
150
+
151
+ # Sets whether to return raw responses.
152
+ #
153
+ # @param value [Boolean] The value to set for raw responses return.
154
+ #
127
155
  def raw=(value)
128
156
  config.return_raw = value
129
157
  end
130
158
 
159
+ # Extracts the content from the last response based on the provider.
160
+ #
161
+ # @return [String] The extracted content.
162
+ # @raise [NotImplementedError] If content extraction is not implemented for the provider.
163
+ #
131
164
  def content
132
165
  case @provider
133
166
  when :localai, :mistral, :ollama, :open_router, :openai
@@ -142,6 +175,13 @@ class AiClient
142
175
  end
143
176
  alias_method :text, :content
144
177
 
178
+ # Handles calls to methods that are missing on the AiClient instance.
179
+ #
180
+ # @param method_name [Symbol] The name of the method called.
181
+ # @param args [Array] Arguments passed to the method.
182
+ # @param block [Proc] Optional block associated with the method call.
183
+ # @return [Object] The result from the underlying client or raises NoMethodError.
184
+ #
145
185
  def method_missing(method_name, *args, &block)
146
186
  if @client.respond_to?(method_name)
147
187
  result = @client.send(method_name, *args, &block)
@@ -152,6 +192,12 @@ class AiClient
152
192
  end
153
193
  end
154
194
 
195
+ # Checks if the instance responds to the missing method.
196
+ #
197
+ # @param method_name [Symbol] The name of the method to check.
198
+ # @param include_private [Boolean] Whether to include private methods in the check.
199
+ # @return [Boolean] True if the method is supported by the client, false otherwise.
200
+ #
155
201
  def respond_to_missing?(method_name, include_private = false)
156
202
  @client.respond_to?(method_name) || super
157
203
  end
@@ -160,6 +206,12 @@ class AiClient
160
206
  ##############################################
161
207
  private
162
208
 
209
+ # Validates the specified provider.
210
+ #
211
+ # @param provider [Symbol] The provider to validate.
212
+ # @return [Symbol, nil] Returns the validated provider or nil.
213
+ # @raise [ArgumentError] If the provider is unsupported.
214
+ #
163
215
  def validate_provider(provider)
164
216
  return nil if provider.nil?
165
217
 
@@ -171,7 +223,11 @@ class AiClient
171
223
  provider
172
224
  end
173
225
 
174
-
226
+ # Creates an instance of the appropriate OmniAI client based on the provider.
227
+ #
228
+ # @return [OmniAI::Client] An instance of the configured OmniAI client.
229
+ # @raise [ArgumentError] If the provider is unsupported.
230
+ #
175
231
  def create_client
176
232
  client_options = {
177
233
  api_key: fetch_api_key,
@@ -209,7 +265,10 @@ class AiClient
209
265
  end
210
266
 
211
267
 
212
- # Similar to fetch_access_tokne but for the instance config
268
+ # Similar to fetch_access_token but for the instance config
269
+ #
270
+ # @return [String, nil] The retrieved API key or nil if not found.
271
+ #
213
272
  def fetch_api_key
214
273
  config.envar_api_key_names[@provider]
215
274
  &.map { |key| ENV[key] }
@@ -217,6 +276,12 @@ class AiClient
217
276
  &.first
218
277
  end
219
278
 
279
+ # Determines the provider based on the provided model.
280
+ #
281
+ # @param model [String] The model name.
282
+ # @return [Symbol] The corresponding provider.
283
+ # @raise [ArgumentError] If the model is unsupported.
284
+ #
220
285
  def determine_provider(model)
221
286
  config.provider_patterns.find { |provider, pattern| model.match?(pattern) }&.first ||
222
287
  raise(ArgumentError, "Unsupported model: #{model}")
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ai_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dewayne VanHoozer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-10 00:00:00.000000000 Z
11
+ date: 2024-10-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: active_hash
@@ -100,14 +100,14 @@ dependencies:
100
100
  requirements:
101
101
  - - ">="
102
102
  - !ruby/object:Gem::Version
103
- version: '0'
103
+ version: 1.8.3
104
104
  type: :runtime
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
- version: '0'
110
+ version: 1.8.3
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: open_router
113
113
  requirement: !ruby/object:Gem::Requirement
@@ -219,6 +219,7 @@ files:
219
219
  - examples/embed.rb
220
220
  - examples/speak.rb
221
221
  - examples/text.rb
222
+ - examples/tools.rb
222
223
  - examples/transcribe.rb
223
224
  - lib/ai_client.rb
224
225
  - lib/ai_client/chat.rb
@@ -228,9 +229,11 @@ files:
228
229
  - lib/ai_client/llm.rb
229
230
  - lib/ai_client/logger_middleware.rb
230
231
  - lib/ai_client/middleware.rb
232
+ - lib/ai_client/models.yml
231
233
  - lib/ai_client/open_router_extensions.rb
232
234
  - lib/ai_client/retry_middleware.rb
233
235
  - lib/ai_client/speak.rb
236
+ - lib/ai_client/tool.rb
234
237
  - lib/ai_client/transcribe.rb
235
238
  - lib/ai_client/version.rb
236
239
  - sig/ai_client.rbs