ai_client 0.2.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 +4 -4
- data/CHANGELOG.md +3 -0
- data/README.md +62 -6
- data/examples/README.md +13 -0
- data/lib/ai_client/config.yml +33 -0
- data/lib/ai_client/configuration.rb +114 -49
- data/lib/ai_client/version.rb +1 -1
- metadata +17 -1
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/CHANGELOG.md
CHANGED
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
|
|
@@ -43,28 +47,71 @@ c2 = AiClient.new('gpt-4o-mini')
|
|
43
47
|
|
44
48
|
### Configuration
|
45
49
|
|
46
|
-
There
|
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
|
47
66
|
|
48
67
|
```ruby
|
49
|
-
AiClient.
|
68
|
+
AiClient.configuration do |config|
|
50
69
|
config.some_item = some_value
|
70
|
+
...
|
51
71
|
end
|
52
72
|
```
|
53
73
|
|
54
|
-
|
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
|
55
94
|
|
56
95
|
```ruby
|
57
96
|
client = AiClient.new('super-ai-overlord-model') do |config|
|
58
97
|
config.some_item = some_value
|
98
|
+
...
|
59
99
|
end
|
60
100
|
```
|
61
101
|
|
62
|
-
|
102
|
+
2. Suppliment from a YAML File
|
63
103
|
|
64
104
|
```ruby
|
65
105
|
client = AiClient.new('baby-model', config: 'path/to/file.yml')
|
66
106
|
```
|
67
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
|
+
|
68
115
|
### What Now?
|
69
116
|
|
70
117
|
TODO: Document the methods and their options.
|
@@ -77,7 +124,7 @@ AI.embed(...)
|
|
77
124
|
AI.batch_embed(...)
|
78
125
|
```
|
79
126
|
|
80
|
-
|
127
|
+
See the [examples directory](examples/README.md) for some ideas on how to use AiClient.
|
81
128
|
|
82
129
|
### System Environment Variables
|
83
130
|
|
@@ -89,6 +136,15 @@ TODO: list all providers supported and their envar
|
|
89
136
|
|
90
137
|
TODO: document the options like `provider: :ollama`
|
91
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
|
+
|
92
148
|
## Contributing
|
93
149
|
|
94
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,27 +1,81 @@
|
|
1
1
|
# ai_client/configuration.rb
|
2
2
|
#
|
3
|
-
#
|
4
|
-
# AiClient.configure do |config|
|
5
|
-
# # global config items that over-ride the defaults
|
6
|
-
# end
|
3
|
+
# AiClient and AiClient::Config
|
7
4
|
#
|
8
|
-
# client
|
9
|
-
#
|
10
|
-
#
|
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')
|
68
|
+
|
11
69
|
|
12
70
|
require 'hashie'
|
13
71
|
require 'logger'
|
72
|
+
require 'yaml'
|
73
|
+
require 'pathname'
|
14
74
|
|
15
75
|
class AiClient
|
16
|
-
# TODO: Use system environment varibles
|
17
|
-
# AI_CLIENT_CONFIG_FILE
|
18
|
-
#
|
19
|
-
# TODO: Config.load('path/to/some_file.yml')
|
20
|
-
# @@default_config (on require from lib/config.yml)
|
21
|
-
# @@config (if the envar exists ?? merge with default)
|
22
|
-
# @config ... done
|
23
|
-
|
24
76
|
class Config < Hashie::Mash
|
77
|
+
DEFAULT_CONFIG_FILEPATH = Pathname.new(__dir__) + 'config.yml'
|
78
|
+
|
25
79
|
include Hashie::Extensions::Mash::PermissiveRespondTo
|
26
80
|
include Hashie::Extensions::Mash::SymbolizeKeys
|
27
81
|
include Hashie::Extensions::Mash::DefineAccessors
|
@@ -36,48 +90,59 @@ class AiClient
|
|
36
90
|
# end
|
37
91
|
# end
|
38
92
|
|
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
|
39
103
|
end
|
40
104
|
|
41
|
-
|
42
|
-
# Class variables to hold default and current config
|
43
|
-
@@default_config = Config.new(
|
44
|
-
logger: Logger.new(STDOUT),
|
45
|
-
timeout: nil,
|
46
|
-
return_raw: false,
|
47
|
-
providers: {},
|
48
|
-
provider_patterns: {
|
49
|
-
anthropic: /^claude/i,
|
50
|
-
openai: /^(gpt|davinci|curie|babbage|ada|whisper|tts|dall-e)/i,
|
51
|
-
google: /^(gemini|palm)/i,
|
52
|
-
mistral: /^(mistral|codestral)/i,
|
53
|
-
localai: /^local-/i,
|
54
|
-
ollama: /(llama|nomic)/i
|
55
|
-
},
|
56
|
-
model_types: {
|
57
|
-
text_to_text: /^(nomic|gpt|davinci|curie|babbage|ada|claude|gemini|palm|command|generate|j2-|mistral|codestral)/i,
|
58
|
-
speech_to_text: /^whisper/i,
|
59
|
-
text_to_speech: /^tts/i,
|
60
|
-
text_to_image: /^dall-e/i
|
61
|
-
}
|
62
|
-
)
|
63
|
-
|
64
|
-
@@class_config = @@default_config.dup
|
65
|
-
|
66
105
|
class << self
|
106
|
+
attr_accessor :class_config, :default_config
|
107
|
+
|
67
108
|
def configure(&block)
|
68
109
|
yield(class_config)
|
69
110
|
end
|
70
111
|
|
71
|
-
|
72
|
-
@@class_config
|
73
|
-
end
|
74
|
-
|
75
|
-
def class_config=(value)
|
76
|
-
@@class_config = value
|
77
|
-
end
|
112
|
+
private
|
78
113
|
|
79
|
-
def
|
80
|
-
|
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
|
81
136
|
end
|
82
137
|
end
|
138
|
+
|
139
|
+
initialize_defaults
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
AiClient.default_config = AiClient::Config.load
|
144
|
+
AiClient.class_config = AiClient.default_config.dup
|
145
|
+
|
146
|
+
if config_file = ENV.fetch('AI_CLIENT_CONFIG_FILE', nil)
|
147
|
+
AiClient.class_config.merge!(AiClient::Config.load(config_file))
|
83
148
|
end
|
data/lib/ai_client/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ai_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dewayne VanHoozer
|
@@ -122,6 +122,20 @@ dependencies:
|
|
122
122
|
- - ">="
|
123
123
|
- !ruby/object:Gem::Version
|
124
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'
|
125
139
|
- !ruby/object:Gem::Dependency
|
126
140
|
name: mocha
|
127
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -161,12 +175,14 @@ files:
|
|
161
175
|
- README.md
|
162
176
|
- Rakefile
|
163
177
|
- examples/Bethany Hamilton.m4a
|
178
|
+
- examples/README.md
|
164
179
|
- examples/common.rb
|
165
180
|
- examples/embed.rb
|
166
181
|
- examples/speak.rb
|
167
182
|
- examples/text.rb
|
168
183
|
- examples/transcribe.rb
|
169
184
|
- lib/ai_client.rb
|
185
|
+
- lib/ai_client/config.yml
|
170
186
|
- lib/ai_client/configuration.rb
|
171
187
|
- lib/ai_client/logger_middleware.rb
|
172
188
|
- lib/ai_client/middleware.rb
|