rubycanusellm 0.3.1 → 0.4.0
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 +9 -0
- data/README.md +16 -4
- data/lib/rubycanusellm/configuration.rb +8 -5
- data/lib/rubycanusellm/providers/mistral.rb +161 -0
- data/lib/rubycanusellm/providers/ollama.rb +142 -0
- data/lib/rubycanusellm/templates/config.rb.tt +7 -2
- data/lib/rubycanusellm/version.rb +1 -1
- data/lib/rubycanusellm.rb +5 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 95279ea6440be4ca1db0588578072aeec9ceb7ed60b3491a9a8a7e2e9f1cd354
|
|
4
|
+
data.tar.gz: d584565619149e8e23a50997345da518c65d1646daee38b5dafecabd76b467cd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f390e611e876ebbe93e4f9a80a2f48126156d620477489fb4de1786db739bfddbc029bbcd3ddfcfc3f06947956ffbec2d942a3dc10430ada08c6a8c754f92673
|
|
7
|
+
data.tar.gz: 95b04a2d9c73571ed57c97ad8f2b752866329ff9f8914eb8324e0b1eeb3000ca963a56b4e31206f7fbec17b5f5ba56874380e98ecc658f4ab43efd3b1f215c0d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.0] - 2026-04-03
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Mistral provider (chat + embeddings)
|
|
8
|
+
- Ollama provider (chat + embeddings, local, no API key required)
|
|
9
|
+
- `config.base_url` for pointing to custom Ollama instances
|
|
10
|
+
- Mistral and Ollama added to `EMBEDDING_PROVIDERS`
|
|
11
|
+
|
|
3
12
|
## [0.3.1] - 2026-04-03
|
|
4
13
|
|
|
5
14
|
### Added
|
data/README.md
CHANGED
|
@@ -67,6 +67,8 @@ That's it. Same code, different provider.
|
|
|
67
67
|
|----------|--------|--------|-------|
|
|
68
68
|
| OpenAI | gpt-4o-mini, gpt-4o, etc. | ✅ | Chat + Embeddings |
|
|
69
69
|
| Anthropic | claude-sonnet-4-20250514, etc. | ✅ | Chat only |
|
|
70
|
+
| Mistral | mistral-small-latest, mistral-large-latest, etc. | ✅ | Chat + Embeddings |
|
|
71
|
+
| Ollama | llama3.2, mistral, etc. | ✅ | Chat + Embeddings (local) |
|
|
70
72
|
| Voyage AI | voyage-3.5, voyage-4, etc. | ✅ | Embeddings only |
|
|
71
73
|
|
|
72
74
|
## API Reference
|
|
@@ -74,15 +76,24 @@ That's it. Same code, different provider.
|
|
|
74
76
|
### Configuration
|
|
75
77
|
```ruby
|
|
76
78
|
RubyCanUseLLM.configure do |config|
|
|
77
|
-
config.provider = :openai # :openai or :
|
|
78
|
-
config.api_key = "your-key" # required
|
|
79
|
+
config.provider = :openai # :openai, :anthropic, :mistral, or :ollama
|
|
80
|
+
config.api_key = "your-key" # required (not needed for Ollama)
|
|
79
81
|
config.model = "gpt-4o-mini" # optional, has sensible defaults
|
|
80
82
|
config.timeout = 30 # optional, default 30s
|
|
83
|
+
config.base_url = "http://localhost:11434" # optional, for Ollama (default shown)
|
|
81
84
|
config.embedding_provider = :voyage # optional, for separate embedding provider
|
|
82
85
|
config.embedding_api_key = "key" # required when embedding_provider is set
|
|
83
86
|
end
|
|
84
87
|
```
|
|
85
88
|
|
|
89
|
+
**Ollama (local, no API key needed):**
|
|
90
|
+
```ruby
|
|
91
|
+
RubyCanUseLLM.configure do |config|
|
|
92
|
+
config.provider = :ollama
|
|
93
|
+
# config.base_url = "http://localhost:11434" # default, change if needed
|
|
94
|
+
end
|
|
95
|
+
```
|
|
96
|
+
|
|
86
97
|
### Chat
|
|
87
98
|
```ruby
|
|
88
99
|
response = RubyCanUseLLM.chat(messages, **options)
|
|
@@ -109,7 +120,7 @@ RubyCanUseLLM.chat(messages, stream: true) do |chunk|
|
|
|
109
120
|
end
|
|
110
121
|
```
|
|
111
122
|
|
|
112
|
-
Each `chunk` is a `RubyCanUseLLM::Chunk` with `content` (the token text) and `role` (`"assistant"`). Works with
|
|
123
|
+
Each `chunk` is a `RubyCanUseLLM::Chunk` with `content` (the token text) and `role` (`"assistant"`). Works with OpenAI, Anthropic, Mistral, and Ollama.
|
|
113
124
|
|
|
114
125
|
### Response
|
|
115
126
|
```ruby
|
|
@@ -205,8 +216,9 @@ end
|
|
|
205
216
|
- [x] Streaming support
|
|
206
217
|
- [x] Embeddings + configurable embedding provider
|
|
207
218
|
- [x] Voyage AI provider (embeddings)
|
|
219
|
+
- [x] Mistral provider (chat + embeddings)
|
|
220
|
+
- [x] Ollama provider (chat + embeddings, local)
|
|
208
221
|
- [ ] `generate:embedding` command
|
|
209
|
-
- [ ] Mistral and Ollama providers
|
|
210
222
|
- [ ] Tool calling
|
|
211
223
|
|
|
212
224
|
## Development
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
module RubyCanUseLLM
|
|
4
4
|
class Configuration
|
|
5
|
-
SUPPORTED_PROVIDERS = %i[openai anthropic voyage].freeze
|
|
6
|
-
EMBEDDING_PROVIDERS = %i[openai voyage].freeze
|
|
5
|
+
SUPPORTED_PROVIDERS = %i[openai anthropic voyage mistral ollama].freeze
|
|
6
|
+
EMBEDDING_PROVIDERS = %i[openai voyage mistral ollama].freeze
|
|
7
7
|
|
|
8
|
-
attr_accessor :provider, :api_key, :model, :timeout, :embedding_provider, :embedding_api_key
|
|
8
|
+
attr_accessor :provider, :api_key, :model, :timeout, :embedding_provider, :embedding_api_key, :base_url
|
|
9
9
|
|
|
10
10
|
def initialize
|
|
11
11
|
@provider = nil
|
|
@@ -14,11 +14,14 @@ module RubyCanUseLLM
|
|
|
14
14
|
@timeout = 30
|
|
15
15
|
@embedding_provider = nil
|
|
16
16
|
@embedding_api_key = nil
|
|
17
|
+
@base_url = nil
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def validate!
|
|
20
|
-
raise Error, "provider is required. Use :openai or :
|
|
21
|
-
|
|
21
|
+
raise Error, "provider is required. Use :openai, :anthropic, :mistral, or :ollama" if provider.nil?
|
|
22
|
+
unless provider == :ollama
|
|
23
|
+
raise Error, "api_key is required" if api_key.nil? || api_key.empty?
|
|
24
|
+
end
|
|
22
25
|
raise Error, "Unknown provider: #{provider}. Supported: #{SUPPORTED_PROVIDERS.join(", ")}" unless SUPPORTED_PROVIDERS.include?(provider)
|
|
23
26
|
end
|
|
24
27
|
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "json"
|
|
5
|
+
require "uri"
|
|
6
|
+
|
|
7
|
+
module RubyCanUseLLM
|
|
8
|
+
module Providers
|
|
9
|
+
class Mistral < Base
|
|
10
|
+
CHAT_URL = "https://api.mistral.ai/v1/chat/completions"
|
|
11
|
+
EMBED_URL = "https://api.mistral.ai/v1/embeddings"
|
|
12
|
+
|
|
13
|
+
def chat(messages, **options, &block)
|
|
14
|
+
if options[:stream] && block
|
|
15
|
+
body = build_body(messages, options.except(:stream)).merge(stream: true)
|
|
16
|
+
stream_request(body, &block)
|
|
17
|
+
else
|
|
18
|
+
body = build_body(messages, options)
|
|
19
|
+
response = request(body)
|
|
20
|
+
parse_response(response)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def embed(text, **options)
|
|
25
|
+
body = {
|
|
26
|
+
model: options[:model] || "mistral-embed",
|
|
27
|
+
input: text
|
|
28
|
+
}
|
|
29
|
+
response = embedding_request(body)
|
|
30
|
+
parse_embedding(response)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def build_body(messages, options)
|
|
36
|
+
{
|
|
37
|
+
model: options[:model] || config.model || "mistral-small-latest",
|
|
38
|
+
messages: format_messages(messages),
|
|
39
|
+
temperature: options[:temperature] || 0.7
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def format_messages(messages)
|
|
44
|
+
messages.map do |msg|
|
|
45
|
+
{ role: msg[:role].to_s, content: msg[:content] }
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def request(body)
|
|
50
|
+
uri = URI(CHAT_URL)
|
|
51
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
52
|
+
http.use_ssl = true
|
|
53
|
+
http.read_timeout = config.timeout
|
|
54
|
+
|
|
55
|
+
req = Net::HTTP::Post.new(uri)
|
|
56
|
+
req["Authorization"] = "Bearer #{config.api_key}"
|
|
57
|
+
req["Content-Type"] = "application/json"
|
|
58
|
+
req.body = body.to_json
|
|
59
|
+
|
|
60
|
+
handle_response(http.request(req))
|
|
61
|
+
rescue Net::ReadTimeout, Net::OpenTimeout
|
|
62
|
+
raise TimeoutError, "Request to Mistral timed out after #{config.timeout}s"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def stream_request(body, &block)
|
|
66
|
+
uri = URI(CHAT_URL)
|
|
67
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
68
|
+
http.use_ssl = true
|
|
69
|
+
http.read_timeout = config.timeout
|
|
70
|
+
|
|
71
|
+
req = Net::HTTP::Post.new(uri)
|
|
72
|
+
req["Authorization"] = "Bearer #{config.api_key}"
|
|
73
|
+
req["Content-Type"] = "application/json"
|
|
74
|
+
req["Accept-Encoding"] = "identity"
|
|
75
|
+
req.body = body.to_json
|
|
76
|
+
|
|
77
|
+
http.request(req) do |response|
|
|
78
|
+
case response.code.to_i
|
|
79
|
+
when 401 then raise AuthenticationError, "Invalid Mistral API key"
|
|
80
|
+
when 429 then raise RateLimitError, "Mistral rate limit exceeded"
|
|
81
|
+
end
|
|
82
|
+
raise ProviderError, "Mistral error (#{response.code})" unless response.code.to_i == 200
|
|
83
|
+
|
|
84
|
+
buffer = ""
|
|
85
|
+
response.read_body do |raw_chunk|
|
|
86
|
+
buffer += raw_chunk
|
|
87
|
+
lines = buffer.split("\n", -1)
|
|
88
|
+
buffer = lines.pop || ""
|
|
89
|
+
lines.each do |line|
|
|
90
|
+
line.chomp!
|
|
91
|
+
next unless line.start_with?("data: ")
|
|
92
|
+
|
|
93
|
+
data = line[6..]
|
|
94
|
+
next if data == "[DONE]"
|
|
95
|
+
|
|
96
|
+
parsed = JSON.parse(data)
|
|
97
|
+
content = parsed.dig("choices", 0, "delta", "content")
|
|
98
|
+
block.call(Chunk.new(content: content)) if content && !content.empty?
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
rescue Net::ReadTimeout, Net::OpenTimeout
|
|
103
|
+
raise TimeoutError, "Request to Mistral timed out after #{config.timeout}s"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def handle_response(response)
|
|
107
|
+
case response.code.to_i
|
|
108
|
+
when 200
|
|
109
|
+
JSON.parse(response.body)
|
|
110
|
+
when 401
|
|
111
|
+
raise AuthenticationError, "Invalid Mistral API key"
|
|
112
|
+
when 429
|
|
113
|
+
raise RateLimitError, "Mistral rate limit exceeded"
|
|
114
|
+
else
|
|
115
|
+
raise ProviderError, "Mistral error (#{response.code}): #{response.body}"
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def parse_response(data)
|
|
120
|
+
choice = data.dig("choices", 0, "message")
|
|
121
|
+
usage = data["usage"]
|
|
122
|
+
|
|
123
|
+
Response.new(
|
|
124
|
+
content: choice["content"],
|
|
125
|
+
model: data["model"],
|
|
126
|
+
input_tokens: usage["prompt_tokens"],
|
|
127
|
+
output_tokens: usage["completion_tokens"],
|
|
128
|
+
raw: data
|
|
129
|
+
)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def embedding_request(body)
|
|
133
|
+
uri = URI(EMBED_URL)
|
|
134
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
135
|
+
http.use_ssl = true
|
|
136
|
+
http.read_timeout = config.timeout
|
|
137
|
+
|
|
138
|
+
req = Net::HTTP::Post.new(uri)
|
|
139
|
+
req["Authorization"] = "Bearer #{config.api_key}"
|
|
140
|
+
req["Content-Type"] = "application/json"
|
|
141
|
+
req.body = body.to_json
|
|
142
|
+
|
|
143
|
+
handle_response(http.request(req))
|
|
144
|
+
rescue Net::ReadTimeout, Net::OpenTimeout
|
|
145
|
+
raise TimeoutError, "Request to Mistral timed out after #{config.timeout}s"
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def parse_embedding(data)
|
|
149
|
+
embedding = data.dig("data", 0, "embedding")
|
|
150
|
+
usage = data["usage"]
|
|
151
|
+
|
|
152
|
+
EmbeddingResponse.new(
|
|
153
|
+
embedding: embedding,
|
|
154
|
+
model: data["model"],
|
|
155
|
+
tokens: usage["total_tokens"],
|
|
156
|
+
raw: data
|
|
157
|
+
)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "json"
|
|
5
|
+
require "uri"
|
|
6
|
+
|
|
7
|
+
module RubyCanUseLLM
|
|
8
|
+
module Providers
|
|
9
|
+
class Ollama < Base
|
|
10
|
+
DEFAULT_BASE_URL = "http://localhost:11434"
|
|
11
|
+
|
|
12
|
+
def chat(messages, **options, &block)
|
|
13
|
+
if options[:stream] && block
|
|
14
|
+
body = build_body(messages, options.except(:stream)).merge(stream: true)
|
|
15
|
+
stream_request(body, &block)
|
|
16
|
+
else
|
|
17
|
+
body = build_body(messages, options).merge(stream: false)
|
|
18
|
+
response = request(body)
|
|
19
|
+
parse_response(response)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def embed(text, **options)
|
|
24
|
+
body = {
|
|
25
|
+
model: options[:model] || "nomic-embed-text",
|
|
26
|
+
input: text
|
|
27
|
+
}
|
|
28
|
+
response = embedding_request(body)
|
|
29
|
+
parse_embedding(response)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def base_url
|
|
35
|
+
config.base_url || DEFAULT_BASE_URL
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def build_body(messages, options)
|
|
39
|
+
{
|
|
40
|
+
model: options[:model] || config.model || "llama3.2",
|
|
41
|
+
messages: format_messages(messages),
|
|
42
|
+
temperature: options[:temperature] || 0.7
|
|
43
|
+
}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def format_messages(messages)
|
|
47
|
+
messages.map do |msg|
|
|
48
|
+
{ role: msg[:role].to_s, content: msg[:content] }
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def request(body)
|
|
53
|
+
uri = URI("#{base_url}/api/chat")
|
|
54
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
55
|
+
http.use_ssl = uri.scheme == "https"
|
|
56
|
+
http.read_timeout = config.timeout
|
|
57
|
+
|
|
58
|
+
req = Net::HTTP::Post.new(uri)
|
|
59
|
+
req["Content-Type"] = "application/json"
|
|
60
|
+
req.body = body.to_json
|
|
61
|
+
|
|
62
|
+
handle_response(http.request(req))
|
|
63
|
+
rescue Net::ReadTimeout, Net::OpenTimeout
|
|
64
|
+
raise TimeoutError, "Request to Ollama timed out after #{config.timeout}s"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def stream_request(body, &block)
|
|
68
|
+
uri = URI("#{base_url}/api/chat")
|
|
69
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
70
|
+
http.use_ssl = uri.scheme == "https"
|
|
71
|
+
http.read_timeout = config.timeout
|
|
72
|
+
|
|
73
|
+
req = Net::HTTP::Post.new(uri)
|
|
74
|
+
req["Content-Type"] = "application/json"
|
|
75
|
+
req["Accept-Encoding"] = "identity"
|
|
76
|
+
req.body = body.to_json
|
|
77
|
+
|
|
78
|
+
http.request(req) do |response|
|
|
79
|
+
raise ProviderError, "Ollama error (#{response.code})" unless response.code.to_i == 200
|
|
80
|
+
|
|
81
|
+
response.read_body do |raw_chunk|
|
|
82
|
+
raw_chunk.split("\n").each do |line|
|
|
83
|
+
line.strip!
|
|
84
|
+
next if line.empty?
|
|
85
|
+
|
|
86
|
+
parsed = JSON.parse(line)
|
|
87
|
+
content = parsed.dig("message", "content")
|
|
88
|
+
block.call(Chunk.new(content: content)) if content && !content.empty? && !parsed["done"]
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
rescue Net::ReadTimeout, Net::OpenTimeout
|
|
93
|
+
raise TimeoutError, "Request to Ollama timed out after #{config.timeout}s"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def handle_response(response)
|
|
97
|
+
case response.code.to_i
|
|
98
|
+
when 200
|
|
99
|
+
JSON.parse(response.body)
|
|
100
|
+
else
|
|
101
|
+
raise ProviderError, "Ollama error (#{response.code}): #{response.body}"
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def parse_response(data)
|
|
106
|
+
message = data["message"]
|
|
107
|
+
Response.new(
|
|
108
|
+
content: message["content"],
|
|
109
|
+
model: data["model"],
|
|
110
|
+
input_tokens: data["prompt_eval_count"] || 0,
|
|
111
|
+
output_tokens: data["eval_count"] || 0,
|
|
112
|
+
raw: data
|
|
113
|
+
)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def embedding_request(body)
|
|
117
|
+
uri = URI("#{base_url}/api/embed")
|
|
118
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
119
|
+
http.use_ssl = uri.scheme == "https"
|
|
120
|
+
http.read_timeout = config.timeout
|
|
121
|
+
|
|
122
|
+
req = Net::HTTP::Post.new(uri)
|
|
123
|
+
req["Content-Type"] = "application/json"
|
|
124
|
+
req.body = body.to_json
|
|
125
|
+
|
|
126
|
+
handle_response(http.request(req))
|
|
127
|
+
rescue Net::ReadTimeout, Net::OpenTimeout
|
|
128
|
+
raise TimeoutError, "Request to Ollama timed out after #{config.timeout}s"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def parse_embedding(data)
|
|
132
|
+
embedding = data.dig("embeddings", 0)
|
|
133
|
+
EmbeddingResponse.new(
|
|
134
|
+
embedding: embedding,
|
|
135
|
+
model: data["model"],
|
|
136
|
+
tokens: data["prompt_eval_count"] || 0,
|
|
137
|
+
raw: data
|
|
138
|
+
)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
RubyCanUseLLM.configure do |config|
|
|
4
|
-
# Choose your provider: :openai or :
|
|
4
|
+
# Choose your provider: :openai, :anthropic, :mistral, or :ollama
|
|
5
5
|
config.provider = :openai
|
|
6
6
|
|
|
7
7
|
# Your API key (use environment variables in production)
|
|
8
|
+
# Not required for Ollama (runs locally)
|
|
8
9
|
config.api_key = ENV["LLM_API_KEY"]
|
|
9
10
|
|
|
10
11
|
# Default model (optional, each provider has a sensible default)
|
|
11
12
|
# OpenAI: "gpt-4o-mini", Anthropic: "claude-sonnet-4-20250514"
|
|
13
|
+
# Mistral: "mistral-small-latest", Ollama: "llama3.2"
|
|
12
14
|
# config.model = "gpt-4o-mini"
|
|
13
15
|
|
|
14
16
|
# Request timeout in seconds (default: 30)
|
|
15
17
|
# config.timeout = 30
|
|
16
18
|
|
|
19
|
+
# Ollama base URL (optional, default: http://localhost:11434)
|
|
20
|
+
# config.base_url = "http://localhost:11434"
|
|
21
|
+
|
|
17
22
|
# Embedding provider (optional, defaults to main provider)
|
|
18
23
|
# Anthropic doesn't support embeddings natively.
|
|
19
|
-
# Use :voyage (recommended by Anthropic) or :
|
|
24
|
+
# Use :voyage (recommended by Anthropic), :openai, :mistral, or :ollama for embeddings.
|
|
20
25
|
# config.embedding_provider = :voyage
|
|
21
26
|
# config.embedding_api_key = ENV["VOYAGE_API_KEY"]
|
|
22
27
|
end
|
data/lib/rubycanusellm.rb
CHANGED
|
@@ -9,13 +9,17 @@ require_relative "rubycanusellm/providers/base"
|
|
|
9
9
|
require_relative "rubycanusellm/providers/openai"
|
|
10
10
|
require_relative "rubycanusellm/providers/anthropic"
|
|
11
11
|
require_relative "rubycanusellm/providers/voyage"
|
|
12
|
+
require_relative "rubycanusellm/providers/mistral"
|
|
13
|
+
require_relative "rubycanusellm/providers/ollama"
|
|
12
14
|
require_relative "rubycanusellm/embedding_response"
|
|
13
15
|
|
|
14
16
|
module RubyCanUseLLM
|
|
15
17
|
PROVIDERS = {
|
|
16
18
|
openai: Providers::OpenAI,
|
|
17
19
|
anthropic: Providers::Anthropic,
|
|
18
|
-
voyage: Providers::Voyage
|
|
20
|
+
voyage: Providers::Voyage,
|
|
21
|
+
mistral: Providers::Mistral,
|
|
22
|
+
ollama: Providers::Ollama
|
|
19
23
|
}.freeze
|
|
20
24
|
|
|
21
25
|
class << self
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubycanusellm
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Juan Manuel Guzman Nava
|
|
@@ -35,6 +35,8 @@ files:
|
|
|
35
35
|
- lib/rubycanusellm/errors.rb
|
|
36
36
|
- lib/rubycanusellm/providers/anthropic.rb
|
|
37
37
|
- lib/rubycanusellm/providers/base.rb
|
|
38
|
+
- lib/rubycanusellm/providers/mistral.rb
|
|
39
|
+
- lib/rubycanusellm/providers/ollama.rb
|
|
38
40
|
- lib/rubycanusellm/providers/openai.rb
|
|
39
41
|
- lib/rubycanusellm/providers/voyage.rb
|
|
40
42
|
- lib/rubycanusellm/response.rb
|