llm.rb 0.1.0 → 0.2.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/README.md +83 -22
- data/lib/llm/conversation.rb +14 -2
- data/lib/llm/core_ext/ostruct.rb +0 -0
- data/lib/llm/error.rb +0 -0
- data/lib/llm/file.rb +0 -0
- data/lib/llm/http_client.rb +0 -0
- data/lib/llm/lazy_conversation.rb +14 -2
- data/lib/llm/message.rb +1 -1
- data/lib/llm/message_queue.rb +0 -0
- data/lib/llm/model.rb +7 -0
- data/lib/llm/provider.rb +117 -98
- data/lib/llm/providers/anthropic/error_handler.rb +1 -1
- data/lib/llm/providers/anthropic/format.rb +0 -0
- data/lib/llm/providers/anthropic/response_parser.rb +0 -0
- data/lib/llm/providers/anthropic.rb +31 -15
- data/lib/llm/providers/gemini/error_handler.rb +0 -0
- data/lib/llm/providers/gemini/format.rb +0 -0
- data/lib/llm/providers/gemini/response_parser.rb +0 -0
- data/lib/llm/providers/gemini.rb +25 -14
- data/lib/llm/providers/ollama/error_handler.rb +0 -0
- data/lib/llm/providers/ollama/format.rb +0 -0
- data/lib/llm/providers/ollama/response_parser.rb +13 -0
- data/lib/llm/providers/ollama.rb +32 -8
- data/lib/llm/providers/openai/error_handler.rb +0 -0
- data/lib/llm/providers/openai/format.rb +0 -0
- data/lib/llm/providers/openai/response_parser.rb +5 -3
- data/lib/llm/providers/openai.rb +22 -12
- data/lib/llm/providers/voyageai/error_handler.rb +32 -0
- data/lib/llm/providers/voyageai/response_parser.rb +13 -0
- data/lib/llm/providers/voyageai.rb +44 -0
- data/lib/llm/response/completion.rb +0 -0
- data/lib/llm/response/embedding.rb +0 -0
- data/lib/llm/response.rb +0 -0
- data/lib/llm/version.rb +1 -1
- data/lib/llm.rb +18 -8
- data/llm.gemspec +6 -1
- data/share/llm/models/anthropic.yml +35 -0
- data/share/llm/models/gemini.yml +35 -0
- data/share/llm/models/ollama.yml +155 -0
- data/share/llm/models/openai.yml +46 -0
- data/spec/anthropic/completion_spec.rb +11 -27
- data/spec/anthropic/embedding_spec.rb +25 -0
- data/spec/gemini/completion_spec.rb +13 -29
- data/spec/gemini/embedding_spec.rb +4 -12
- data/spec/llm/lazy_conversation_spec.rb +45 -63
- data/spec/ollama/completion_spec.rb +7 -16
- data/spec/ollama/embedding_spec.rb +14 -5
- data/spec/openai/completion_spec.rb +19 -43
- data/spec/openai/embedding_spec.rb +4 -12
- data/spec/readme_spec.rb +9 -12
- data/spec/setup.rb +7 -16
- metadata +81 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a7f9330df025e16999629b094202d78b504de9bedeafc45ec8d75bf729024567
|
4
|
+
data.tar.gz: e3a22daa5ac7add9b815346fe810ed368b9f3e3a3bb135cb4e5bbbb3875eacae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b985c6f0b967354f3d1e5941ebeb2e5d6623b9d0abc33fd238f3764e72a6a6e10c293e7e61221d1be8ad89e631ecc7236bca597e809f1598ad54f0b305bb1103
|
7
|
+
data.tar.gz: 721f373b9eada6417b864e723504e5168df8f4362bef9edb4026aad2b39a3f99de5c72081f40ab80828d2f53d24c62c55e035ccd4964e3dadca2a27f3784e6e6
|
data/README.md
CHANGED
@@ -51,26 +51,26 @@ belonging to a lazy conversation:
|
|
51
51
|
require "llm"
|
52
52
|
|
53
53
|
llm = LLM.openai(ENV["KEY"])
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
54
|
+
convo = llm.chat File.read("./share/llm/prompts/system.txt"), :system
|
55
|
+
convo.chat "Tell me the answer to 5 + 15"
|
56
|
+
convo.chat "Tell me the answer to (5 + 15) * 2"
|
57
|
+
convo.chat "Tell me the answer to ((5 + 15) * 2) / 10"
|
58
|
+
convo.messages.each { print "[#{_1.role}] ", _1.content, "\n" }
|
59
59
|
|
60
60
|
##
|
61
|
-
# [system] You are
|
62
|
-
#
|
61
|
+
# [system] You are my math assistant.
|
62
|
+
# I will provide you with (simple) equations.
|
63
|
+
# You will provide answers in the format "The answer to <equation> is <answer>".
|
63
64
|
# I will provide you a set of messages. Reply to all of them.
|
64
65
|
# A message is considered unanswered if there is no corresponding assistant response.
|
65
66
|
#
|
66
|
-
# [user]
|
67
|
-
# [user]
|
68
|
-
# [user]
|
67
|
+
# [user] Tell me the answer to 5 + 15
|
68
|
+
# [user] Tell me the answer to (5 + 15) * 2
|
69
|
+
# [user] Tell me the answer to ((5 + 15) * 2) / 10
|
69
70
|
#
|
70
|
-
# [assistant] The
|
71
|
-
#
|
72
|
-
#
|
73
|
-
# Because it ran out of juice! 🍊😂
|
71
|
+
# [assistant] The answer to 5 + 15 is 20.
|
72
|
+
# The answer to (5 + 15) * 2 is 40.
|
73
|
+
# The answer to ((5 + 15) * 2) / 10 is 4.
|
74
74
|
```
|
75
75
|
|
76
76
|
#### Prompts
|
@@ -99,28 +99,87 @@ provider accepts:
|
|
99
99
|
|
100
100
|
The
|
101
101
|
[`LLM::Provider#embed`](https://0x1eef.github.io/x/llm/LLM/Provider.html#embed-instance_method)
|
102
|
-
method generates a vector representation of
|
103
|
-
Embeddings capture the semantic meaning of text
|
104
|
-
|
105
|
-
|
106
|
-
of text
|
102
|
+
method generates a vector representation of one or more chunks
|
103
|
+
of text. Embeddings capture the semantic meaning of text –
|
104
|
+
a common use-case for them is to store chunks of text in a
|
105
|
+
vector database, and then to query the database for *semantically
|
106
|
+
similar* text. These chunks of similar text can then support the
|
107
|
+
generation of a prompt that is used to query a large language model,
|
108
|
+
which will go on to generate a response.
|
109
|
+
|
110
|
+
For example, a user query might find similar text that adds important
|
111
|
+
context to the prompt that informs the large language model in how to respond.
|
112
|
+
The chunks of text may also carry metadata that can be used to further filter
|
113
|
+
and contextualize the search results. This technique is popularly known as
|
114
|
+
retrieval-augmented generation (RAG). Embeddings can also be used for
|
115
|
+
other purposes as well – RAG is just one of the most popular use-cases.
|
116
|
+
|
117
|
+
Let's take a look at an example that generates a couple of vectors
|
118
|
+
for two chunks of text:
|
107
119
|
|
108
120
|
```ruby
|
109
121
|
#!/usr/bin/env ruby
|
110
122
|
require "llm"
|
111
123
|
|
112
124
|
llm = LLM.openai(ENV["KEY"])
|
113
|
-
res = llm.embed("
|
125
|
+
res = llm.embed(["programming is fun", "ruby is a programming language"])
|
114
126
|
print res.class, "\n"
|
115
127
|
print res.embeddings.size, "\n"
|
116
128
|
print res.embeddings[0].size, "\n"
|
117
129
|
|
118
130
|
##
|
119
131
|
# LLM::Response::Embedding
|
120
|
-
#
|
132
|
+
# 2
|
121
133
|
# 1536
|
122
134
|
```
|
123
135
|
|
136
|
+
### LLM
|
137
|
+
|
138
|
+
#### Timeouts
|
139
|
+
|
140
|
+
When running the ollama provider locally it might take a while for
|
141
|
+
the language model to reply – depending on hardware and the
|
142
|
+
size of the model. The following example demonstrates how to wait
|
143
|
+
a longer period of time for a response through the use of the
|
144
|
+
`timeout` configuration option with the `qwq` model. The following
|
145
|
+
example waits up to 15 minutes for a response:
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
#!/usr/bin/env ruby
|
149
|
+
require "llm"
|
150
|
+
|
151
|
+
llm = LLM.ollama(nil, timeout: 60*15)
|
152
|
+
llm.chat "What is the meaning of life ?", model: "qwq"
|
153
|
+
llm.last_message.tap { print "[assistant] ", _1.content, "\n" }
|
154
|
+
```
|
155
|
+
|
156
|
+
#### Models
|
157
|
+
|
158
|
+
Generally each Large Language Model provides multiple models to choose
|
159
|
+
from, and each model has its own set of capabilities and limitations.
|
160
|
+
The following example demonstrates how to query the list of models
|
161
|
+
through the
|
162
|
+
[LLM::Provider#models](http://0x1eef.github.io/x/llm/LLM/Provider.html#models-instance_method)
|
163
|
+
method – the example happens to use the ollama provider but
|
164
|
+
this can be done for any provider:
|
165
|
+
|
166
|
+
```ruby
|
167
|
+
#!/usr/bin/env ruby
|
168
|
+
require "llm"
|
169
|
+
|
170
|
+
##
|
171
|
+
# List models
|
172
|
+
llm = LLM.ollama(nil)
|
173
|
+
llm.models.each { print "#{_2.name}: #{_2.description}", "\n" }
|
174
|
+
|
175
|
+
##
|
176
|
+
# Select a model
|
177
|
+
llm.chat "Hello, world!", model: llm.models["qwq"]
|
178
|
+
|
179
|
+
##
|
180
|
+
# This also works
|
181
|
+
llm.chat "Hello, world!", model: "qwq"
|
182
|
+
```
|
124
183
|
## Providers
|
125
184
|
|
126
185
|
- [x] [Anthropic](https://www.anthropic.com/)
|
@@ -139,7 +198,9 @@ A complete API reference is available at [0x1eef.github.io/x/llm](https://0x1eef
|
|
139
198
|
|
140
199
|
## Install
|
141
200
|
|
142
|
-
|
201
|
+
llm.rb can be installed via rubygems.org:
|
202
|
+
|
203
|
+
gem install llm.rb
|
143
204
|
|
144
205
|
## License
|
145
206
|
|
data/lib/llm/conversation.rb
CHANGED
@@ -20,8 +20,9 @@ module LLM
|
|
20
20
|
##
|
21
21
|
# @param [LLM::Provider] provider
|
22
22
|
# A provider
|
23
|
-
def initialize(provider)
|
23
|
+
def initialize(provider, params = {})
|
24
24
|
@provider = provider
|
25
|
+
@params = params
|
25
26
|
@messages = []
|
26
27
|
end
|
27
28
|
|
@@ -30,9 +31,20 @@ module LLM
|
|
30
31
|
# @return [LLM::Conversation]
|
31
32
|
def chat(prompt, role = :user, **params)
|
32
33
|
tap do
|
33
|
-
completion = @provider.complete(prompt, role,
|
34
|
+
completion = @provider.complete(prompt, role, **@params.merge(params.merge(messages:)))
|
34
35
|
@messages.concat [Message.new(role, prompt), completion.choices[0]]
|
35
36
|
end
|
36
37
|
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# @param [#to_s] role
|
41
|
+
# The role of the last message.
|
42
|
+
# Defaults to the LLM's assistant role (eg "assistant" or "model")
|
43
|
+
# @return [LLM::Message]
|
44
|
+
# The last message for the given role
|
45
|
+
def last_message(role: @provider.assistant_role)
|
46
|
+
messages.reverse_each.find { _1.role == role.to_s }
|
47
|
+
end
|
48
|
+
alias_method :recent_message, :last_message
|
37
49
|
end
|
38
50
|
end
|
data/lib/llm/core_ext/ostruct.rb
CHANGED
File without changes
|
data/lib/llm/error.rb
CHANGED
File without changes
|
data/lib/llm/file.rb
CHANGED
File without changes
|
data/lib/llm/http_client.rb
CHANGED
File without changes
|
@@ -24,8 +24,9 @@ module LLM
|
|
24
24
|
##
|
25
25
|
# @param [LLM::Provider] provider
|
26
26
|
# A provider
|
27
|
-
def initialize(provider)
|
27
|
+
def initialize(provider, params = {})
|
28
28
|
@provider = provider
|
29
|
+
@params = params
|
29
30
|
@messages = LLM::MessageQueue.new(provider)
|
30
31
|
end
|
31
32
|
|
@@ -33,7 +34,18 @@ module LLM
|
|
33
34
|
# @param prompt (see LLM::Provider#prompt)
|
34
35
|
# @return [LLM::Conversation]
|
35
36
|
def chat(prompt, role = :user, **params)
|
36
|
-
tap { @messages << [prompt, role, params] }
|
37
|
+
tap { @messages << [prompt, role, @params.merge(params)] }
|
37
38
|
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# @param [#to_s] role
|
42
|
+
# The role of the last message.
|
43
|
+
# Defaults to the LLM's assistant role (eg "assistant" or "model")
|
44
|
+
# @return [LLM::Message]
|
45
|
+
# The last message for the given role
|
46
|
+
def last_message(role: @provider.assistant_role)
|
47
|
+
messages.reverse_each.find { _1.role == role.to_s }
|
48
|
+
end
|
49
|
+
alias_method :recent_message, :last_message
|
38
50
|
end
|
39
51
|
end
|
data/lib/llm/message.rb
CHANGED
data/lib/llm/message_queue.rb
CHANGED
File without changes
|
data/lib/llm/model.rb
ADDED
data/lib/llm/provider.rb
CHANGED
@@ -1,114 +1,133 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
##
|
4
|
+
# The Provider class represents an abstract class for
|
5
|
+
# LLM (Language Model) providers
|
6
|
+
class LLM::Provider
|
7
|
+
require_relative "http_client"
|
8
|
+
include LLM::HTTPClient
|
9
|
+
|
5
10
|
##
|
6
|
-
#
|
7
|
-
#
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
@http = Net::HTTP.new(host, port).tap do |http|
|
20
|
-
http.use_ssl = ssl
|
21
|
-
end
|
11
|
+
# @param [String] secret
|
12
|
+
# The secret key for authentication
|
13
|
+
# @param [String] host
|
14
|
+
# The host address of the LLM provider
|
15
|
+
# @param [Integer] port
|
16
|
+
# The port number
|
17
|
+
# @param [Integer] timeout
|
18
|
+
# The number of seconds to wait for a response
|
19
|
+
def initialize(secret, host:, port: 443, timeout: 60, ssl: true)
|
20
|
+
@secret = secret
|
21
|
+
@http = Net::HTTP.new(host, port).tap do |http|
|
22
|
+
http.use_ssl = ssl
|
23
|
+
http.read_timeout = timeout
|
22
24
|
end
|
25
|
+
end
|
23
26
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
##
|
28
|
+
# Returns an inspection of the provider object
|
29
|
+
# @return [String]
|
30
|
+
# @note The secret key is redacted in inspect for security reasons
|
31
|
+
def inspect
|
32
|
+
"#<#{self.class.name}:0x#{object_id.to_s(16)} @secret=[REDACTED] @http=#{@http.inspect}>"
|
33
|
+
end
|
31
34
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
35
|
+
##
|
36
|
+
# @param [String, Array<String>] input
|
37
|
+
# The input to embed
|
38
|
+
# @raise [NotImplementedError]
|
39
|
+
# When the method is not implemented by a subclass
|
40
|
+
# @return [LLM::Response::Embedding]
|
41
|
+
def embed(input, **params)
|
42
|
+
raise NotImplementedError
|
43
|
+
end
|
41
44
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
45
|
+
##
|
46
|
+
# Completes a given prompt using the LLM
|
47
|
+
# @param [String] prompt
|
48
|
+
# The input prompt to be completed
|
49
|
+
# @param [Symbol] role
|
50
|
+
# The role of the prompt (e.g. :user, :system)
|
51
|
+
# @raise [NotImplementedError]
|
52
|
+
# When the method is not implemented by a subclass
|
53
|
+
# @return [LLM::Response::Completion]
|
54
|
+
def complete(prompt, role = :user, **params)
|
55
|
+
raise NotImplementedError
|
56
|
+
end
|
54
57
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
58
|
+
##
|
59
|
+
# Starts a new lazy conversation
|
60
|
+
# @param prompt (see LLM::Provider#complete)
|
61
|
+
# @param role (see LLM::Provider#complete)
|
62
|
+
# @raise (see LLM::Provider#complete)
|
63
|
+
# @return [LLM::LazyConversation]
|
64
|
+
def chat(prompt, role = :user, **params)
|
65
|
+
LLM::LazyConversation.new(self, params).chat(prompt, role)
|
66
|
+
end
|
64
67
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
68
|
+
##
|
69
|
+
# Starts a new conversation
|
70
|
+
# @param prompt (see LLM::Provider#complete)
|
71
|
+
# @param role (see LLM::Provider#complete)
|
72
|
+
# @raise (see LLM::Provider#complete)
|
73
|
+
# @return [LLM::Conversation]
|
74
|
+
def chat!(prompt, role = :user, **params)
|
75
|
+
LLM::Conversation.new(self, params).chat(prompt, role)
|
76
|
+
end
|
74
77
|
|
75
|
-
|
78
|
+
##
|
79
|
+
# @return [String]
|
80
|
+
# Returns the role of the assistant in the conversation.
|
81
|
+
# Usually "assistant" or "model"
|
82
|
+
def assistant_role
|
83
|
+
raise NotImplementedError
|
84
|
+
end
|
76
85
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
def auth(req)
|
84
|
-
raise NotImplementedError
|
85
|
-
end
|
86
|
+
##
|
87
|
+
# @return [Hash<String, LLM::Model>]
|
88
|
+
# Returns a hash of available models
|
89
|
+
def models
|
90
|
+
raise NotImplementedError
|
91
|
+
end
|
86
92
|
|
87
|
-
|
88
|
-
# @return [Module]
|
89
|
-
# Returns the module responsible for parsing a successful LLM response
|
90
|
-
# @raise [NotImplementedError]
|
91
|
-
# (see LLM::Provider#complete)
|
92
|
-
def response_parser
|
93
|
-
raise NotImplementedError
|
94
|
-
end
|
93
|
+
private
|
95
94
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
end
|
95
|
+
##
|
96
|
+
# The headers to include with a request
|
97
|
+
# @raise [NotImplementedError]
|
98
|
+
# (see LLM::Provider#complete)
|
99
|
+
def headers
|
100
|
+
raise NotImplementedError
|
101
|
+
end
|
104
102
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
103
|
+
##
|
104
|
+
# @return [Module]
|
105
|
+
# Returns the module responsible for parsing a successful LLM response
|
106
|
+
# @raise [NotImplementedError]
|
107
|
+
# (see LLM::Provider#complete)
|
108
|
+
def response_parser
|
109
|
+
raise NotImplementedError
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# @return [Class]
|
114
|
+
# Returns the class responsible for handling an unsuccessful LLM response
|
115
|
+
# @raise [NotImplementedError]
|
116
|
+
# (see LLM::Provider#complete)
|
117
|
+
def error_handler
|
118
|
+
raise NotImplementedError
|
119
|
+
end
|
120
|
+
|
121
|
+
##
|
122
|
+
# @param [String] provider
|
123
|
+
# The name of the provider
|
124
|
+
# @return [Hash<String, Hash>]
|
125
|
+
def load_models!(provider)
|
126
|
+
require "yaml" unless defined?(YAML)
|
127
|
+
rootdir = File.realpath File.join(__dir__, "..", "..")
|
128
|
+
sharedir = File.join(rootdir, "share", "llm")
|
129
|
+
provider = provider.gsub(/[^a-z0-9]/i, "")
|
130
|
+
yaml = File.join(sharedir, "models", "#{provider}.yml")
|
131
|
+
YAML.safe_load_file(yaml).transform_values { LLM::Model.new(_1) }
|
113
132
|
end
|
114
133
|
end
|
@@ -20,7 +20,7 @@ class LLM::Anthropic
|
|
20
20
|
# Raises a subclass of {LLM::Error LLM::Error}
|
21
21
|
def raise_error!
|
22
22
|
case res
|
23
|
-
when Net::
|
23
|
+
when Net::HTTPUnauthorized
|
24
24
|
raise LLM::Error::Unauthorized.new { _1.response = res }, "Authentication error"
|
25
25
|
when Net::HTTPTooManyRequests
|
26
26
|
raise LLM::Error::RateLimit.new { _1.response = res }, "Too many requests"
|
File without changes
|
File without changes
|
@@ -11,7 +11,6 @@ module LLM
|
|
11
11
|
include Format
|
12
12
|
|
13
13
|
HOST = "api.anthropic.com"
|
14
|
-
DEFAULT_PARAMS = {max_tokens: 1024, model: "claude-3-5-sonnet-20240620"}.freeze
|
15
14
|
|
16
15
|
##
|
17
16
|
# @param secret (see LLM::Provider#initialize)
|
@@ -20,14 +19,17 @@ module LLM
|
|
20
19
|
end
|
21
20
|
|
22
21
|
##
|
22
|
+
# Provides an embedding via VoyageAI per
|
23
|
+
# [Anthropic's recommendation](https://docs.anthropic.com/en/docs/build-with-claude/embeddings)
|
23
24
|
# @param input (see LLM::Provider#embed)
|
25
|
+
# @param [String] token
|
26
|
+
# Valid token for the VoyageAI API
|
27
|
+
# @param [Hash] params
|
28
|
+
# Additional parameters to pass to the API
|
24
29
|
# @return (see LLM::Provider#embed)
|
25
|
-
def embed(input, **params)
|
26
|
-
|
27
|
-
|
28
|
-
req = preflight(req, body)
|
29
|
-
res = request(@http, req)
|
30
|
-
Response::Embedding.new(res).extend(response_parser)
|
30
|
+
def embed(input, token:, **params)
|
31
|
+
llm = LLM.voyageai(token)
|
32
|
+
llm.embed(input, **params)
|
31
33
|
end
|
32
34
|
|
33
35
|
##
|
@@ -36,20 +38,34 @@ module LLM
|
|
36
38
|
# @param role (see LLM::Provider#complete)
|
37
39
|
# @return (see LLM::Provider#complete)
|
38
40
|
def complete(prompt, role = :user, **params)
|
39
|
-
|
41
|
+
params = {max_tokens: 1024, model: "claude-3-5-sonnet-20240620"}.merge!(params)
|
42
|
+
req = Net::HTTP::Post.new("/v1/messages", headers)
|
40
43
|
messages = [*(params.delete(:messages) || []), Message.new(role, prompt)]
|
41
|
-
|
42
|
-
|
43
|
-
req = preflight(req, body)
|
44
|
-
res = request(@http, req)
|
44
|
+
req.body = JSON.dump({messages: format(messages)}.merge!(params))
|
45
|
+
res = request(@http, req)
|
45
46
|
Response::Completion.new(res).extend(response_parser)
|
46
47
|
end
|
47
48
|
|
49
|
+
##
|
50
|
+
# @return (see LLM::Provider#assistant_role)
|
51
|
+
def assistant_role
|
52
|
+
"assistant"
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# @return (see LLM::Provider#models)
|
57
|
+
def models
|
58
|
+
@models ||= load_models!("anthropic")
|
59
|
+
end
|
60
|
+
|
48
61
|
private
|
49
62
|
|
50
|
-
def
|
51
|
-
|
52
|
-
|
63
|
+
def headers
|
64
|
+
{
|
65
|
+
"Content-Type" => "application/json",
|
66
|
+
"x-api-key" => @secret,
|
67
|
+
"anthropic-version" => "2023-06-01"
|
68
|
+
}
|
53
69
|
end
|
54
70
|
|
55
71
|
def response_parser
|
File without changes
|
File without changes
|
File without changes
|
data/lib/llm/providers/gemini.rb
CHANGED
@@ -11,7 +11,6 @@ module LLM
|
|
11
11
|
include Format
|
12
12
|
|
13
13
|
HOST = "generativelanguage.googleapis.com"
|
14
|
-
DEFAULT_PARAMS = {model: "gemini-1.5-flash"}.freeze
|
15
14
|
|
16
15
|
##
|
17
16
|
# @param secret (see LLM::Provider#initialize)
|
@@ -23,11 +22,10 @@ module LLM
|
|
23
22
|
# @param input (see LLM::Provider#embed)
|
24
23
|
# @return (see LLM::Provider#embed)
|
25
24
|
def embed(input, **params)
|
26
|
-
path = ["/v1beta/models
|
27
|
-
req = Net::HTTP::Post.new
|
28
|
-
body = {content: {parts: [{text: input}]}}
|
29
|
-
|
30
|
-
res = request @http, req
|
25
|
+
path = ["/v1beta/models/text-embedding-004", "embedContent?key=#{@secret}"].join(":")
|
26
|
+
req = Net::HTTP::Post.new(path, headers)
|
27
|
+
req.body = JSON.dump({content: {parts: [{text: input}]}})
|
28
|
+
res = request(@http, req)
|
31
29
|
Response::Embedding.new(res).extend(response_parser)
|
32
30
|
end
|
33
31
|
|
@@ -37,20 +35,33 @@ module LLM
|
|
37
35
|
# @param role (see LLM::Provider#complete)
|
38
36
|
# @return (see LLM::Provider#complete)
|
39
37
|
def complete(prompt, role = :user, **params)
|
40
|
-
params
|
41
|
-
path
|
42
|
-
req
|
38
|
+
params = {model: "gemini-1.5-flash"}.merge!(params)
|
39
|
+
path = ["/v1beta/models/#{params.delete(:model)}", "generateContent?key=#{@secret}"].join(":")
|
40
|
+
req = Net::HTTP::Post.new(path, headers)
|
43
41
|
messages = [*(params.delete(:messages) || []), LLM::Message.new(role, prompt)]
|
44
|
-
body = {contents: format(messages)}
|
45
|
-
|
46
|
-
res = request(@http, req)
|
42
|
+
req.body = JSON.dump({contents: format(messages)})
|
43
|
+
res = request(@http, req)
|
47
44
|
Response::Completion.new(res).extend(response_parser)
|
48
45
|
end
|
49
46
|
|
47
|
+
##
|
48
|
+
# @return (see LLM::Provider#assistant_role)
|
49
|
+
def assistant_role
|
50
|
+
"model"
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# @return (see LLM::Provider#models)
|
55
|
+
def models
|
56
|
+
@models ||= load_models!("gemini")
|
57
|
+
end
|
58
|
+
|
50
59
|
private
|
51
60
|
|
52
|
-
def
|
53
|
-
|
61
|
+
def headers
|
62
|
+
{
|
63
|
+
"Content-Type" => "application/json"
|
64
|
+
}
|
54
65
|
end
|
55
66
|
|
56
67
|
def response_parser
|
File without changes
|
File without changes
|