ai_client 0.1.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.irbrc +3 -0
- data/CHANGELOG.md +8 -1
- data/README.md +82 -2
- data/examples/README.md +13 -0
- data/lib/ai_client/config.yml +33 -0
- data/lib/ai_client/configuration.rb +130 -66
- data/lib/ai_client/middleware.rb +37 -0
- data/lib/ai_client/version.rb +1 -1
- data/lib/ai_client.rb +43 -37
- metadata +34 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c938cc076640fa6952b1140252b0c234a44e7dfd6ee24d2485c344d3b2c98880
|
4
|
+
data.tar.gz: 1dc5cb21b49c2a731689e7ab7d4f6cce56392b06ecb83e2d54b535ec0d0b3d27
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d2d81aa7bc979a8c75856965261ce54470ef3df1586eb65e809c2e4d32f8a4771a22a85640e026260826ab691e54742666ce0b9d71f2ba992b60db3d5c689cc8
|
7
|
+
data.tar.gz: 7347641ccb974d62c0393a364cb09b214019c0e99a2514478632a9b0c4736bf530c0ba415bae316b0438230691418265b21aed46ef32ca122935d73e30085f41
|
data/.irbrc
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
-
##
|
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**
|
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
|
-
|
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.
|
data/examples/README.md
ADDED
@@ -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
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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(
|
108
|
+
def configure(&block)
|
109
|
+
yield(class_config)
|
34
110
|
end
|
35
111
|
|
36
|
-
|
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
|
-
|
46
|
-
|
47
|
-
|
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
|
data/lib/ai_client/version.rb
CHANGED
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
|
-
|
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,
|
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
|
-
|
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.
|
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
|
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-
|
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
|