ai_client 0.2.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/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
|