ai_client 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b12bef21b23a46a47cc409042afd5eaae36d6cf1cdd2fae47f12fd0b958ed961
4
- data.tar.gz: 0c70c7f7e562da50876ee65c1f1de81f6ee7d37e97a2a7c114bb692ce5d6155d
3
+ metadata.gz: c938cc076640fa6952b1140252b0c234a44e7dfd6ee24d2485c344d3b2c98880
4
+ data.tar.gz: 1dc5cb21b49c2a731689e7ab7d4f6cce56392b06ecb83e2d54b535ec0d0b3d27
5
5
  SHA512:
6
- metadata.gz: 56d14adae8ab29719083dc7bb63edf33fa625237274edef043b3738f39757fa0c15983232c9aab0a2afbf8db33cbcafc89ed0cb39d0180d045e3c2f6f467a5fe
7
- data.tar.gz: 5c3d314a10a6aa6f10a51b6f8eaf574dea6d108feb5b5ec64f47fb0e494cc1ebe865a7b2fbc9401aada23c692cae2346a48dbcf062e84a202e080f25f0790d9d
6
+ metadata.gz: d2d81aa7bc979a8c75856965261ce54470ef3df1586eb65e809c2e4d32f8a4771a22a85640e026260826ab691e54742666ce0b9d71f2ba992b60db3d5c689cc8
7
+ data.tar.gz: 7347641ccb974d62c0393a364cb09b214019c0e99a2514478632a9b0c4736bf530c0ba415bae316b0438230691418265b21aed46ef32ca122935d73e30085f41
data/.irbrc ADDED
@@ -0,0 +1,3 @@
1
+ # .irbrc
2
+
3
+ require_relative 'lib/ai_client'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  ## [Unreleased]
2
2
 
3
- ## [0.1.0] - 2024-10-02
3
+ ## Released
4
+ ### [0.2.1] - 2024-10-05
5
+ - Added support for YAML configuration files
6
+
7
+ ### [0.2.0] - 2024-10-04
8
+ - Configuration is more robust. Still room for improvement.
9
+
10
+ ### [0.1.0] - 2024-10-02
4
11
 
5
12
  - Initial working release
data/README.md CHANGED
@@ -2,7 +2,11 @@
2
2
 
3
3
  First and foremost a big **THANK YOU** to [Kevin Sylvestre](https://ksylvest.com/) for his gem [OmniAI](https://github.com/ksylvest/omniai) upon which this effort depends.
4
4
 
5
- **This is a work in progress** Its implemented as a class rather than the typical module for most gems. The `AiClient::Configuration` class is a little first draft-ish. I'm looking to bulk it up a lot. At this point I think some of the current tests are failing; but, over all `AiClien` is working. I've used early versions of it in several projects.
5
+ **This is a work in progress** I could use your help extending its capability.
6
+
7
+ AiClien` is working. I've used early versions of it in several projects.
8
+
9
+ See the [change log](CHANGELOG.md) for recent modifications.
6
10
 
7
11
  ## Summary
8
12
 
@@ -41,6 +45,73 @@ c1 = AiClient.new('nomic-embeddings-text')
41
45
  c2 = AiClient.new('gpt-4o-mini')
42
46
  ```
43
47
 
48
+ ### Configuration
49
+
50
+ There are three levels of configuration, each inherenting from the level above. The following sections
51
+ describe those configuration levels.
52
+
53
+ #### Default Configuration
54
+
55
+ The file [lib/ai_client/configuration.rb] hard codes the default configuration. This is used to
56
+ update the [lib/ai_client/config.yml] file during development. If you have
57
+ some changes for this configuration please send me a pull request so we
58
+ can all benefit from your efforts.
59
+
60
+ #### Class Configuration
61
+
62
+ The class configuration is derived initially from the default configuration. It
63
+ can be changed in three ways.
64
+
65
+ 1. Class Configuration Block
66
+
67
+ ```ruby
68
+ AiClient.configuration do |config|
69
+ config.some_item = some_value
70
+ ...
71
+ end
72
+ ```
73
+
74
+ 2. Set by a Config File
75
+
76
+ ```ruby
77
+ AiClient.class_config = AiClient::Config.load('path/to/file.yml')
78
+ ```
79
+
80
+ 3. Supplemented by a Config File
81
+
82
+ ```ruby
83
+ AiClient.class_config.merge! AiClient::Config.load('path/to/file.yml')
84
+ ```
85
+
86
+ #### Instance Configuration
87
+
88
+ All instances have a configuration. Initially that configuration is the same
89
+ as the class configuration; however, each instance can have its own separate
90
+ configuration. For an instance the class configuration can either be supplemented
91
+ or complete over-ridden.
92
+
93
+ 1. Supplement from a Constructor Block
94
+
95
+ ```ruby
96
+ client = AiClient.new('super-ai-overlord-model') do |config|
97
+ config.some_item = some_value
98
+ ...
99
+ end
100
+ ```
101
+
102
+ 2. Suppliment from a YAML File
103
+
104
+ ```ruby
105
+ client = AiClient.new('baby-model', config: 'path/to/file.yml')
106
+ ```
107
+
108
+ 3. Load Complete Configuration from a YAML File
109
+
110
+ ```ruby
111
+ client = AiClient.new('your-model')
112
+ client.config = AiClient::Config.load('path/to/file.yml')
113
+ ```
114
+
44
115
  ### What Now?
45
116
 
46
117
  TODO: Document the methods and their options.
@@ -53,7 +124,7 @@ AI.embed(...)
53
124
  AI.batch_embed(...)
54
125
  ```
55
126
 
56
- TODO: see the [examples] directory.
127
+ See the [examples directory](examples/README.md) for some ideas on how to use AiClient.
57
128
 
58
129
  ### System Environment Variables
59
130
 
@@ -65,6 +136,15 @@ TODO: list all providers supported and their envar
65
136
 
66
137
  TODO: document the options like `provider: :ollama`
67
138
 
139
+ ## Extensions for OmniAI
140
+
141
+ The AiClient makes use of extensions to the OmniAI gem that define
142
+ additional providers and protocols.
143
+
144
+ 1. **OmniAI::Ollama^** which wraps the OmniAI::OpenAI class
145
+ 2. **OmniAI::LocalAI** which also wraps the OmniAI::OpenAI class
146
+ 3. **OmniAI::OpenRouter** TODO: Still under development
147
+
68
148
  ## Contributing
69
149
 
70
150
  I can sure use your help. This industry is moving faster than I can keep up with. If you have a bug fix or new feature idea then have at it. Send me a pull request so we all can benefit from your efforts.
@@ -0,0 +1,13 @@
1
+ # Examples
2
+
3
+ | File | Content |
4
+ | --- | --- |
5
+ | Bethany Hamilton.m4a | Audio file used to demonstrate transcribe|
6
+ | common.rb | Stuff used by each example |
7
+ | embed.rb | Demonstrates using Ollama locally to vectorize text for embeddings|
8
+ | speak.rb | Demonstrates using OpenAI's text to speech models |
9
+ | text.rb | Demonstrates text-to-text transformers "chat" |
10
+ | transcribe.rb | Uses OpenAI's audio-to-text model |
11
+
12
+ Many of these example programs show both the raw response object as well as just the
13
+ content from the response.
@@ -0,0 +1,33 @@
1
+ ---
2
+ :logger: !ruby/object:Logger
3
+ level: 0
4
+ progname:
5
+ default_formatter: !ruby/object:Logger::Formatter
6
+ datetime_format:
7
+ formatter:
8
+ logdev: !ruby/object:Logger::LogDevice
9
+ shift_period_suffix:
10
+ shift_size:
11
+ shift_age:
12
+ filename:
13
+ dev: !ruby/object:IO {}
14
+ binmode: false
15
+ reraise_write_errors: []
16
+ mon_data: !ruby/object:Monitor {}
17
+ mon_data_owner_object_id: 920
18
+ level_override: {}
19
+ :timeout:
20
+ :return_raw: false
21
+ :providers: {}
22
+ :provider_patterns:
23
+ :anthropic: !ruby/regexp /^claude/i
24
+ :openai: !ruby/regexp /^(gpt|davinci|curie|babbage|ada|whisper|tts|dall-e)/i
25
+ :google: !ruby/regexp /^(gemini|palm)/i
26
+ :mistral: !ruby/regexp /^(mistral|codestral)/i
27
+ :localai: !ruby/regexp /^local-/i
28
+ :ollama: !ruby/regexp /(llama|nomic)/i
29
+ :model_types:
30
+ :text_to_text: !ruby/regexp /^(nomic|gpt|davinci|curie|babbage|ada|claude|gemini|palm|command|generate|j2-|mistral|codestral)/i
31
+ :speech_to_text: !ruby/regexp /^whisper/i
32
+ :text_to_speech: !ruby/regexp /^tts/i
33
+ :text_to_image: !ruby/regexp /^dall-e/i
@@ -1,84 +1,148 @@
1
1
  # ai_client/configuration.rb
2
+ #
3
+ # AiClient and AiClient::Config
4
+ #
5
+ # The AiClient class provides a configurable client for interacting with various AI service providers.
6
+ # It allows users to set global configurations and provider-specific settings via a block.
7
+ #
8
+ # There are three levels of configuration:
9
+ # * default_config .. the starting point
10
+ # * class_config .... for all instances
11
+ # * config .......... for an instance
12
+ #
13
+ # Class Configuration
14
+ # starts with the default configuration but can
15
+ # be changed in three different ways.
16
+ # 1. Use the configuration block
17
+ # AiClient.configuration do |config|
18
+ # some_item = some_value
19
+ # ...
20
+ # end
21
+ #
22
+ # 2. Automatic YAML configuration file
23
+ # Set the system environment variable AI_CLIENT_CONFIG_FILE
24
+ # to an existing configuration file. The contents of that
25
+ # file will be automatically merged on top of the
26
+ # default configuration.
27
+ #
28
+ # 3. Manual YAML configuration file
29
+ # You can completely replace the class configuration
30
+ # AiClient.class_config = AiClient::Config.load('path/to/file.yml')
31
+ # You can supplement the existing class config
32
+ # AiClient.class_config.merge!(AiClient::Config.load('path/to/file.yml'))
33
+ #
34
+ # Instance Configuration
35
+ # AiClient is setup so that you can have multiple instance
36
+ # of clients each using a different model / provider and having
37
+ # a different configuration. There are several ways you
38
+ # can manipulate an instance's configuration.
39
+ #
40
+ # 1. The default instance configuration inherents from the
41
+ # the class configuration.
42
+ # client = AiClient.new('your_model')
43
+ # You can access the instance configuration using
44
+ # client.config.some_item
45
+ # client.config[:some_item]
46
+ # client.config['some_item']
47
+ # All three ways returns the value for that configuration item.
48
+ # To change the value of an item its also that simple.
49
+ # client.config.some_item = some_value
50
+ # client.config[:some_item] = some_value
51
+ # client.config['some_item'] = some_value
52
+ # 2. Instance constructor block
53
+ # client = AiClient.new('your_model') do |config|
54
+ # config.some_item = some_value
55
+ # ...
56
+ # end
57
+ #
58
+ # 3. Like the class configuration you can can replace or
59
+ # supplement an instance's configuration from a YAML file.
60
+ # client = AiClient.new('your_model', config: 'path/to/file.yml')
61
+ # client.config.merge!(AiClient::Config.load('path/to/file.yml'))
62
+ # Both of those example suppliment / over0ride items in
63
+ # the class configuration to become the instance's
64
+ # configuration. To completely replace the instance's
65
+ # configuration you can do this.
66
+ # client = AiClient.new('your_model')
67
+ # client.config = AiClient::Config.load('path/to/file.yml')
2
68
 
69
+
70
+ require 'hashie'
3
71
  require 'logger'
72
+ require 'yaml'
73
+ require 'pathname'
4
74
 
5
75
  class AiClient
6
- # TODO: Need a centralized service where
7
- # metadata about LLMs are available
8
- # via and API call. Would hope that
9
- # the providers would add a "list"
10
- # endpoint to their API which would
11
- # return the metadata for all of their
12
- # models.
76
+ class Config < Hashie::Mash
77
+ DEFAULT_CONFIG_FILEPATH = Pathname.new(__dir__) + 'config.yml'
78
+
79
+ include Hashie::Extensions::Mash::PermissiveRespondTo
80
+ include Hashie::Extensions::Mash::SymbolizeKeys
81
+ include Hashie::Extensions::Mash::DefineAccessors
82
+
13
83
 
14
- PROVIDER_PATTERNS = {
15
- anthropic: /^claude/i,
16
- openai: /^(gpt|davinci|curie|babbage|ada|whisper|tts|dall-e)/i,
17
- google: /^(gemini|palm)/i,
18
- mistral: /^(mistral|codestral)/i,
19
- localai: /^local-/i,
20
- ollama: /(llama-|nomic)/i
21
- }
84
+ # I'm not sure about this ...
85
+ # def provider(name, &block)
86
+ # if block_given?
87
+ # providers[name] = block.call
88
+ # else
89
+ # providers[name] || {}
90
+ # end
91
+ # end
22
92
 
23
- MODEL_TYPES = {
24
- text_to_text: /^(nomic|gpt|davinci|curie|babbage|ada|claude|gemini|palm|command|generate|j2-|mistral|codestral)/i,
25
- speech_to_text: /^whisper/i,
26
- text_to_speech: /^tts/i,
27
- text_to_image: /^dall-e/i
28
- }
93
+ class << self
94
+ def load(filepath=DEFAULT_CONFIG_FILEPATH)
95
+ filepath = Pathname.new(filepath) unless Pathname == filepath.class
96
+ if filepath.exist?
97
+ new(YAML.parse(filepath.read).to_ruby)
98
+ else
99
+ raise ArgumentError, "#{filepath} does not exist"
100
+ end
101
+ end
102
+ end
103
+ end
29
104
 
30
105
  class << self
106
+ attr_accessor :class_config, :default_config
31
107
 
32
- def configure
33
- yield(configuration)
108
+ def configure(&block)
109
+ yield(class_config)
34
110
  end
35
111
 
36
- def configuration
37
- @configuration ||= Configuration.new
38
- end
112
+ private
39
113
 
114
+ def initialize_defaults
115
+ @default_config = Config.new(
116
+ logger: Logger.new(STDOUT),
117
+ timeout: nil,
118
+ return_raw: false,
119
+ providers: {},
120
+ provider_patterns: {
121
+ anthropic: /^claude/i,
122
+ openai: /^(gpt|davinci|curie|babbage|ada|whisper|tts|dall-e)/i,
123
+ google: /^(gemini|palm)/i,
124
+ mistral: /^(mistral|codestral)/i,
125
+ localai: /^local-/i,
126
+ ollama: /(llama|nomic)/i
127
+ },
128
+ model_types: {
129
+ text_to_text: /^(nomic|gpt|davinci|curie|babbage|ada|claude|gemini|palm|command|generate|j2-|mistral|codestral)/i,
130
+ speech_to_text: /^whisper/i,
131
+ text_to_speech: /^tts/i,
132
+ text_to_image: /^dall-e/i
133
+ }
134
+ )
135
+ @class_config = @default_config.dup
136
+ end
40
137
  end
41
138
 
139
+ initialize_defaults
140
+ end
42
141
 
43
142
 
143
+ AiClient.default_config = AiClient::Config.load
144
+ AiClient.class_config = AiClient.default_config.dup
44
145
 
45
- # Usage example:
46
- # Configure general settings
47
- # AiClient.configure do |config|
48
- # config.logger = Logger.new('ai_client.log')
49
- # config.return_raw = true
50
- # end
51
- #
52
- # Configure provider-specific settings
53
- # AiClient.configure do |config|
54
- # config.configure_provider(:openai) do
55
- # {
56
- # organization: 'org-123',
57
- # api_version: 'v1'
58
- # }
59
- # end
60
- # end
61
- #
62
-
63
- class Configuration
64
- attr_accessor :logger, :timeout, :return_raw
65
- attr_reader :providers, :provider_patterns, :model_types
66
-
67
- def initialize
68
- @logger = Logger.new(STDOUT)
69
- @timeout = nil
70
- @return_raw = false
71
- @providers = {}
72
- @provider_patterns = AiClient::PROVIDER_PATTERNS.dup
73
- @model_types = AiClient::MODEL_TYPES.dup
74
- end
75
-
76
- def provider(name, &block)
77
- if block_given?
78
- @providers[name] = block
79
- else
80
- @providers[name]&.call || {}
81
- end
82
- end
83
- end
84
- end
146
+ if config_file = ENV.fetch('AI_CLIENT_CONFIG_FILE', nil)
147
+ AiClient.class_config.merge!(AiClient::Config.load(config_file))
148
+ end
@@ -0,0 +1,37 @@
1
+ # lib/ai_client/middleware.rb
2
+
3
+ # TODO: As concurrently designed the middleware must
4
+ # be set before an instance of AiClient is created.
5
+ # Any `use` commands for middleware made after
6
+ # the instance is created will not be available
7
+ # to that instance.
8
+ # Change this so that middleware can be added
9
+ # and removed from an existing client.
10
+
11
+
12
+ class AiClient
13
+
14
+ def call_with_middlewares(method, *args, **kwargs, &block)
15
+ stack = self.class.middlewares.reverse.reduce(-> { send(method, *args, **kwargs, &block) }) do |next_middleware, middleware|
16
+ -> { middleware.call(self, next_middleware, *args, **kwargs) }
17
+ end
18
+ stack.call
19
+ end
20
+
21
+
22
+ class << self
23
+
24
+ def middlewares
25
+ @middlewares ||= []
26
+ end
27
+
28
+ def use(middleware)
29
+ middlewares << middleware
30
+ end
31
+
32
+ def clear_middlewares
33
+ @middlewares = []
34
+ end
35
+ end
36
+
37
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class AiClient
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.1"
5
5
  end
data/lib/ai_client.rb CHANGED
@@ -17,6 +17,7 @@ require_relative 'extensions/omniai-ollama'
17
17
  require_relative 'extensions/omniai-localai'
18
18
 
19
19
  require_relative 'ai_client/configuration'
20
+ require_relative 'ai_client/middleware'
20
21
  require_relative 'ai_client/version'
21
22
 
22
23
  # Create a generic client instance using only model name
@@ -26,25 +27,53 @@ require_relative 'ai_client/version'
26
27
  # AiClient.use(RetryMiddleware.new(max_retries: 5, base_delay: 2, max_delay: 30))
27
28
  # AiClient.use(LoggingMiddleware.new(AiClient.configuration.logger))
28
29
  #
29
- # TODO: As concurrently designed the middleware must
30
- # be set before an instance of AiClient is created.
31
- # Any `use` commands for middleware made after
32
- # the instance is created will not be available
33
- # to that instance.
34
- # Change this so that middleware can be added
35
- # and removed from an existing client.
30
+
36
31
 
37
32
  class AiClient
38
33
 
39
- attr_reader :client, :provider, :model, :model_type, :logger, :last_response, :config
34
+ attr_reader :client, # OmniAI's client instance
35
+ :provider, # [Symbol]
36
+ :model, # [String]
37
+ :model_type, # [Symbol]
38
+ :logger,
39
+ :last_response,
40
+ :timeout,
41
+ :config # Instance configuration
42
+
43
+ # You can over-ride the class config by providing a block like this
44
+ # c = AiClient.new(...) do |config|
45
+ # config.logger = nil
46
+ # end
47
+ #
48
+ # You can also load an instance's config from a YAML file.
49
+ # c = AiClient.new('model_name'. cpmfog: 'path/to/file.yml', ...)
50
+ #
51
+ # ... and you can do both = load from a file and
52
+ # over-ride with a config block
53
+ #
54
+ # The options object is basically those things that the
55
+ # OmniAI clients want to see.
56
+ #
57
+ def initialize(model, **options, &block)
58
+ # Assign the instance variable @config from the class variable @@config
59
+ @config = self.class.class_config.dup
60
+
61
+ # Yield the @config to a block if given
62
+ yield(@config) if block_given?
63
+
64
+ # Merge in an instance-specific YAML file
65
+ if options.has_key?(:config)
66
+ @config.merge! Config.load(options[:config])
67
+ options.delete(:config) # Lconfig not supported by OmniAI
68
+ end
69
+
70
+ @model = model
71
+ explicit_provider = options.fetch(:provider, config.provider)
40
72
 
41
- def initialize(model, config: Configuration.new, **options)
42
- @model = model
43
- @config = config
44
- @provider = validate_provider(options[:provider]) || determine_provider(model)
73
+ @provider = validate_provider(explicit_provider) || determine_provider(model)
45
74
  @model_type = determine_model_type(model)
46
75
 
47
- provider_config = @config.provider(@provider)
76
+ provider_config = @config.providers[@provider] || {}
48
77
 
49
78
  @logger = options[:logger] || @config.logger
50
79
  @timeout = options[:timeout] || @config.timeout
@@ -58,6 +87,7 @@ class AiClient
58
87
  end
59
88
 
60
89
 
90
+
61
91
  def response = last_response
62
92
  def raw? = config.return_raw
63
93
 
@@ -71,7 +101,6 @@ class AiClient
71
101
  def chat(messages, **params)
72
102
  result = call_with_middlewares(:chat_without_middlewares, messages, **params)
73
103
  @last_response = result
74
- # debug_me print " (raw: #{raw?}) "
75
104
  raw? ? result : content
76
105
  end
77
106
 
@@ -114,14 +143,6 @@ class AiClient
114
143
  ######################################
115
144
  ## Utilities
116
145
 
117
- def call_with_middlewares(method, *args, **kwargs, &block)
118
- stack = self.class.middlewares.reverse.reduce(-> { send(method, *args, **kwargs, &block) }) do |next_middleware, middleware|
119
- -> { middleware.call(self, next_middleware, *args, **kwargs) }
120
- end
121
- stack.call
122
- end
123
-
124
-
125
146
  def content
126
147
  case @provider
127
148
  when :openai, :localai, :ollama
@@ -141,21 +162,6 @@ class AiClient
141
162
  ##############################################
142
163
  ## Public Class Methods
143
164
 
144
- class << self
145
-
146
- def middlewares
147
- @middlewares ||= []
148
- end
149
-
150
- def use(middleware)
151
- middlewares << middleware
152
- end
153
-
154
- def clear_middlewares
155
- @middlewares = []
156
- end
157
- end
158
-
159
165
  def method_missing(method_name, *args, &block)
160
166
  if @client.respond_to?(method_name)
161
167
  result = @client.send(method_name, *args, &block)
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ai_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.1
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-03 00:00:00.000000000 Z
11
+ date: 2024-10-05 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: hashie
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: omniai
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -108,6 +122,20 @@ dependencies:
108
122
  - - ">="
109
123
  - !ruby/object:Gem::Version
110
124
  version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: hashdiff
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
111
139
  - !ruby/object:Gem::Dependency
112
140
  name: mocha
113
141
  requirement: !ruby/object:Gem::Requirement
@@ -141,19 +169,23 @@ extensions: []
141
169
  extra_rdoc_files: []
142
170
  files:
143
171
  - ".envrc"
172
+ - ".irbrc"
144
173
  - CHANGELOG.md
145
174
  - LICENSE
146
175
  - README.md
147
176
  - Rakefile
148
177
  - examples/Bethany Hamilton.m4a
178
+ - examples/README.md
149
179
  - examples/common.rb
150
180
  - examples/embed.rb
151
181
  - examples/speak.rb
152
182
  - examples/text.rb
153
183
  - examples/transcribe.rb
154
184
  - lib/ai_client.rb
185
+ - lib/ai_client/config.yml
155
186
  - lib/ai_client/configuration.rb
156
187
  - lib/ai_client/logger_middleware.rb
188
+ - lib/ai_client/middleware.rb
157
189
  - lib/ai_client/retry_middleware.rb
158
190
  - lib/ai_client/version.rb
159
191
  - lib/extensions/omniai-localai.rb