langchainrb 0.3.2 → 0.3.4
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/Gemfile +2 -0
- data/Gemfile.lock +57 -10
- data/README.md +26 -5
- data/Rakefile +5 -0
- data/examples/create_and_manage_few_shot_prompt_templates.rb +3 -3
- data/examples/store_and_query_with_pinecone.rb +5 -2
- data/examples/store_and_query_with_qdrant.rb +4 -2
- data/examples/store_and_query_with_weaviate.rb +4 -1
- data/lib/agent/chain_of_thought_agent/chain_of_thought_agent.rb +13 -14
- data/lib/dependency_helper.rb +18 -0
- data/lib/langchain.rb +3 -0
- data/lib/llm/base.rb +3 -3
- data/lib/llm/cohere.rb +5 -5
- data/lib/llm/hugging_face.rb +32 -0
- data/lib/llm/openai.rb +4 -4
- data/lib/logging.rb +13 -0
- data/lib/prompt/base.rb +3 -4
- data/lib/prompt/loading.rb +3 -3
- data/lib/tool/base.rb +20 -8
- data/lib/tool/calculator.rb +11 -5
- data/lib/tool/serp_api.rb +25 -13
- data/lib/tool/wikipedia.rb +15 -8
- data/lib/vectorsearch/base.rb +5 -3
- data/lib/vectorsearch/milvus.rb +9 -24
- data/lib/vectorsearch/pinecone.rb +10 -20
- data/lib/vectorsearch/qdrant.rb +8 -15
- data/lib/vectorsearch/weaviate.rb +11 -26
- data/lib/version.rb +1 -1
- metadata +46 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a5a782dd2282ab5dd4aed3f1d0e421a4f9b227fa4c5450ed27f2f98a86af74f4
|
4
|
+
data.tar.gz: b47bb5d6789d7abb81f56ee1beb0b52323184f578475aec3e92fcc19b4a1314a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e918b49b2b04a0e7009db732a5b24ab080a0d8d6b4c3be4084aa3a1492ddd6c1627d467b8bff4b34e1e5160b71622a75772ef92658dce2589b9542b8b0a8137
|
7
|
+
data.tar.gz: 3b465f1a05e614d64d416582aeaf7d998bf51422705331b220e08b00774d4beed4dcbc4e2a71b81bad87cddfe8c8f4c8db421d650e7161f16b26a1259922f71c
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.3.4] - 2023-05-16
|
4
|
+
- LLMs
|
5
|
+
- Introducing support for HuggingFace
|
6
|
+
|
7
|
+
## [0.3.3] - 2023-05-16
|
8
|
+
- Dependencies are now optionally loaded and required at runtime
|
9
|
+
- Start using `standardrb` for linting
|
10
|
+
- Use the Ruby logger
|
11
|
+
|
3
12
|
## [0.3.2] - 2023-05-15
|
4
13
|
- Agents
|
5
14
|
- Fix Chain of Thought prompt loader
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,16 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
langchainrb (0.3.
|
5
|
-
cohere-ruby (~> 0.9.3)
|
6
|
-
eqn (~> 1.6.5)
|
7
|
-
google_search_results (~> 2.0.0)
|
8
|
-
milvus (~> 0.9.0)
|
9
|
-
pinecone (~> 0.1.6)
|
10
|
-
qdrant-ruby (~> 0.9.0)
|
11
|
-
ruby-openai (~> 4.0.0)
|
12
|
-
weaviate-ruby (~> 0.8.0)
|
13
|
-
wikipedia-client (~> 1.17.0)
|
4
|
+
langchainrb (0.3.4)
|
14
5
|
|
15
6
|
GEM
|
16
7
|
remote: https://rubygems.org/
|
@@ -35,6 +26,7 @@ GEM
|
|
35
26
|
tzinfo (~> 2.0)
|
36
27
|
addressable (2.8.4)
|
37
28
|
public_suffix (>= 2.0.2, < 6.0)
|
29
|
+
ast (2.4.2)
|
38
30
|
builder (3.2.4)
|
39
31
|
byebug (11.1.3)
|
40
32
|
coderay (1.1.3)
|
@@ -125,9 +117,14 @@ GEM
|
|
125
117
|
httparty (0.21.0)
|
126
118
|
mini_mime (>= 1.0.0)
|
127
119
|
multi_xml (>= 0.5.2)
|
120
|
+
hugging-face (0.3.2)
|
121
|
+
faraday (~> 1.0)
|
128
122
|
i18n (1.13.0)
|
129
123
|
concurrent-ruby (~> 1.0)
|
130
124
|
ice_nine (0.11.2)
|
125
|
+
json (2.6.3)
|
126
|
+
language_server-protocol (3.17.0.3)
|
127
|
+
lint_roller (1.0.0)
|
131
128
|
loofah (2.21.1)
|
132
129
|
crass (~> 1.0.2)
|
133
130
|
nokogiri (>= 1.5.9)
|
@@ -138,10 +135,15 @@ GEM
|
|
138
135
|
minitest (5.18.0)
|
139
136
|
multi_xml (0.6.0)
|
140
137
|
multipart-post (2.3.0)
|
138
|
+
nokogiri (1.14.3-arm64-darwin)
|
139
|
+
racc (~> 1.4)
|
141
140
|
nokogiri (1.14.3-x86_64-darwin)
|
142
141
|
racc (~> 1.4)
|
143
142
|
nokogiri (1.14.3-x86_64-linux)
|
144
143
|
racc (~> 1.4)
|
144
|
+
parallel (1.23.0)
|
145
|
+
parser (3.2.2.1)
|
146
|
+
ast (~> 2.4.1)
|
145
147
|
pinecone (0.1.71)
|
146
148
|
dry-struct (~> 1.6.0)
|
147
149
|
dry-validation (~> 1.10.0)
|
@@ -173,7 +175,10 @@ GEM
|
|
173
175
|
rake (>= 12.2)
|
174
176
|
thor (~> 1.0)
|
175
177
|
zeitwerk (~> 2.5)
|
178
|
+
rainbow (3.1.1)
|
176
179
|
rake (13.0.6)
|
180
|
+
regexp_parser (2.8.0)
|
181
|
+
rexml (3.2.5)
|
177
182
|
rspec (3.12.0)
|
178
183
|
rspec-core (~> 3.12.0)
|
179
184
|
rspec-expectations (~> 3.12.0)
|
@@ -187,15 +192,45 @@ GEM
|
|
187
192
|
diff-lcs (>= 1.2.0, < 2.0)
|
188
193
|
rspec-support (~> 3.12.0)
|
189
194
|
rspec-support (3.12.0)
|
195
|
+
rubocop (1.50.2)
|
196
|
+
json (~> 2.3)
|
197
|
+
parallel (~> 1.10)
|
198
|
+
parser (>= 3.2.0.0)
|
199
|
+
rainbow (>= 2.2.2, < 4.0)
|
200
|
+
regexp_parser (>= 1.8, < 3.0)
|
201
|
+
rexml (>= 3.2.5, < 4.0)
|
202
|
+
rubocop-ast (>= 1.28.0, < 2.0)
|
203
|
+
ruby-progressbar (~> 1.7)
|
204
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
205
|
+
rubocop-ast (1.28.1)
|
206
|
+
parser (>= 3.2.1.0)
|
207
|
+
rubocop-performance (1.16.0)
|
208
|
+
rubocop (>= 1.7.0, < 2.0)
|
209
|
+
rubocop-ast (>= 0.4.0)
|
190
210
|
ruby-openai (4.0.0)
|
191
211
|
faraday (>= 1)
|
192
212
|
faraday-multipart (>= 1)
|
213
|
+
ruby-progressbar (1.13.0)
|
193
214
|
ruby2_keywords (0.0.5)
|
215
|
+
standard (1.28.2)
|
216
|
+
language_server-protocol (~> 3.17.0.2)
|
217
|
+
lint_roller (~> 1.0)
|
218
|
+
rubocop (~> 1.50.2)
|
219
|
+
standard-custom (~> 1.0.0)
|
220
|
+
standard-performance (~> 1.0.1)
|
221
|
+
standard-custom (1.0.0)
|
222
|
+
lint_roller (~> 1.0)
|
223
|
+
standard-performance (1.0.1)
|
224
|
+
lint_roller (~> 1.0)
|
225
|
+
rubocop-performance (~> 1.16.0)
|
226
|
+
standardrb (1.0.1)
|
227
|
+
standard
|
194
228
|
thor (1.2.1)
|
195
229
|
treetop (1.6.12)
|
196
230
|
polyglot (~> 0.3)
|
197
231
|
tzinfo (2.0.6)
|
198
232
|
concurrent-ruby (~> 1.0)
|
233
|
+
unicode-display_width (2.4.2)
|
199
234
|
weaviate-ruby (0.8.1)
|
200
235
|
faraday (~> 1)
|
201
236
|
faraday_middleware (~> 1)
|
@@ -205,15 +240,27 @@ GEM
|
|
205
240
|
zeitwerk (2.6.8)
|
206
241
|
|
207
242
|
PLATFORMS
|
243
|
+
arm64-darwin-22
|
208
244
|
x86_64-darwin-19
|
209
245
|
x86_64-linux
|
210
246
|
|
211
247
|
DEPENDENCIES
|
248
|
+
cohere-ruby (~> 0.9.3)
|
212
249
|
dotenv-rails (~> 2.7.6)
|
250
|
+
eqn (~> 1.6.5)
|
251
|
+
google_search_results (~> 2.0.0)
|
252
|
+
hugging-face (~> 0.3.2)
|
213
253
|
langchainrb!
|
254
|
+
milvus (~> 0.9.0)
|
255
|
+
pinecone (~> 0.1.6)
|
214
256
|
pry-byebug (~> 3.10.0)
|
257
|
+
qdrant-ruby (~> 0.9.0)
|
215
258
|
rake (~> 13.0)
|
216
259
|
rspec (~> 3.0)
|
260
|
+
ruby-openai (~> 4.0.0)
|
261
|
+
standardrb
|
262
|
+
weaviate-ruby (~> 0.8.0)
|
263
|
+
wikipedia-client (~> 1.17.0)
|
217
264
|
|
218
265
|
BUNDLED WITH
|
219
266
|
2.4.0
|
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
🦜️🔗 LangChain.rb
|
2
|
-
---
|
2
|
+
---
|
3
3
|
⚡ Building applications with LLMs through composability ⚡
|
4
4
|
|
5
5
|
👨💻👩💻 CURRENTLY SEEKING PEOPLE TO FORM THE CORE GROUP OF MAINTAINERS WITH
|
@@ -39,6 +39,8 @@ require "langchain"
|
|
39
39
|
|
40
40
|
Choose the LLM provider you'll be using (OpenAI or Cohere) and retrieve the API key.
|
41
41
|
|
42
|
+
Add `gem "weaviate-ruby", "~> 0.8.0"` to your Gemfile.
|
43
|
+
|
42
44
|
Pick the vector search database you'll be using and instantiate the client:
|
43
45
|
```ruby
|
44
46
|
client = Vectorsearch::Weaviate.new(
|
@@ -49,9 +51,9 @@ client = Vectorsearch::Weaviate.new(
|
|
49
51
|
)
|
50
52
|
|
51
53
|
# You can instantiate any other supported vector search database:
|
52
|
-
client = Vectorsearch::Milvus.new(...)
|
53
|
-
client = Vectorsearch::Qdrant.new(...)
|
54
|
-
client = Vectorsearch::Pinecone.new(...)
|
54
|
+
client = Vectorsearch::Milvus.new(...) # `gem "milvus", "~> 0.9.0"`
|
55
|
+
client = Vectorsearch::Qdrant.new(...) # `gem"qdrant-ruby", "~> 0.9.0"`
|
56
|
+
client = Vectorsearch::Pinecone.new(...) # `gem "pinecone", "~> 0.1.6"`
|
55
57
|
```
|
56
58
|
|
57
59
|
```ruby
|
@@ -92,6 +94,8 @@ client.ask(
|
|
92
94
|
|
93
95
|
### Using Standalone LLMs 🗣️
|
94
96
|
|
97
|
+
Add `gem "ruby-openai", "~> 4.0.0"` to your Gemfile.
|
98
|
+
|
95
99
|
#### OpenAI
|
96
100
|
```ruby
|
97
101
|
openai = LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
|
@@ -104,6 +108,8 @@ openai.complete(prompt: "What is the meaning of life?")
|
|
104
108
|
```
|
105
109
|
|
106
110
|
#### Cohere
|
111
|
+
Add `gem "cohere-ruby", "~> 0.9.3"` to your Gemfile.
|
112
|
+
|
107
113
|
```ruby
|
108
114
|
cohere = LLM::Cohere.new(api_key: ENV["COHERE_API_KEY"])
|
109
115
|
```
|
@@ -114,6 +120,9 @@ cohere.embed(text: "foo bar")
|
|
114
120
|
cohere.complete(prompt: "What is the meaning of life?")
|
115
121
|
```
|
116
122
|
|
123
|
+
#### HuggingFace
|
124
|
+
Add `gem "hugging-face", "~> 0.3.2"` to your Gemfile.
|
125
|
+
|
117
126
|
### Using Prompts 📋
|
118
127
|
|
119
128
|
#### Prompt Templates
|
@@ -204,6 +213,8 @@ Agents are semi-autonomous bots that can respond to user questions and use avail
|
|
204
213
|
|
205
214
|
#### Chain-of-Thought Agent
|
206
215
|
|
216
|
+
Add `gem "openai-ruby"`, `gem "eqn"`, and `gem "google_search_results"` to your Gemfile
|
217
|
+
|
207
218
|
```ruby
|
208
219
|
agent = Agent::ChainOfThoughtAgent.new(llm: :openai, llm_api_key: ENV["OPENAI_API_KEY"], tools: ['search', 'calculator'])
|
209
220
|
|
@@ -211,7 +222,7 @@ agent.tools
|
|
211
222
|
# => ["search", "calculator"]
|
212
223
|
```
|
213
224
|
```ruby
|
214
|
-
agent.run(question: "How many full soccer fields would be needed to cover the distance between NYC and DC in a straight line?"
|
225
|
+
agent.run(question: "How many full soccer fields would be needed to cover the distance between NYC and DC in a straight line?")
|
215
226
|
#=> "Approximately 2,945 soccer fields would be needed to cover the distance between NYC and DC in a straight line."
|
216
227
|
```
|
217
228
|
|
@@ -228,6 +239,16 @@ agent.run(question: "How many full soccer fields would be needed to cover the di
|
|
228
239
|
| "search" | A wrapper around Google Search | `ENV["SERPAPI_API_KEY"]` (https://serpapi.com/manage-api-key)
|
229
240
|
| "wikipedia" | Calls Wikipedia API to retrieve the summary | |
|
230
241
|
|
242
|
+
|
243
|
+
## Logging
|
244
|
+
|
245
|
+
LangChain.rb uses standard logging mechanisms and defaults to `:debug` level. Most messages are at info level, but we will add debug or warn statements as needed.
|
246
|
+
To show all log messages:
|
247
|
+
|
248
|
+
```ruby
|
249
|
+
Langchain.logger.level = :info
|
250
|
+
```
|
251
|
+
|
231
252
|
## Development
|
232
253
|
|
233
254
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/Rakefile
CHANGED
@@ -9,10 +9,10 @@ prompt = Prompt::FewShotPromptTemplate.new(
|
|
9
9
|
template: "Input: {input}\nOutput: {output}"
|
10
10
|
),
|
11
11
|
examples: [
|
12
|
-
{
|
13
|
-
{
|
12
|
+
{input: "happy", output: "sad"},
|
13
|
+
{input: "tall", output: "short"}
|
14
14
|
],
|
15
|
-
|
15
|
+
input_variables: ["adjective"]
|
16
16
|
)
|
17
17
|
|
18
18
|
prompt.format(adjective: "good")
|
@@ -1,5 +1,8 @@
|
|
1
1
|
require "langchain"
|
2
2
|
|
3
|
+
# gem install pinecone
|
4
|
+
# or add `gem "pinecone"` to your Gemfile
|
5
|
+
|
3
6
|
# Instantiate the Qdrant client
|
4
7
|
pinecone = Vectorsearch::Pinecone.new(
|
5
8
|
environment: ENV["PINECONE_ENVIRONMENT"],
|
@@ -35,9 +38,9 @@ pinecone.ask(
|
|
35
38
|
)
|
36
39
|
|
37
40
|
# Generate your an embedding and search by it
|
38
|
-
openai = LLM::OpenAI.new(api_key: ENV[
|
41
|
+
openai = LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
|
39
42
|
embedding = openai.embed(text: "veggie")
|
40
43
|
|
41
44
|
pinecone.similarity_search_by_vector(
|
42
45
|
embedding: embedding
|
43
|
-
)
|
46
|
+
)
|
@@ -1,5 +1,8 @@
|
|
1
1
|
require "langchain"
|
2
2
|
|
3
|
+
# gem install qdrant-ruby
|
4
|
+
# or add `gem "qdrant-ruby"` to your Gemfile
|
5
|
+
|
3
6
|
# Instantiate the Qdrant client
|
4
7
|
qdrant = Vectorsearch::Qdrant.new(
|
5
8
|
url: ENV["QDRANT_URL"],
|
@@ -9,7 +12,6 @@ qdrant = Vectorsearch::Qdrant.new(
|
|
9
12
|
llm_api_key: ENV["COHERE_API_KEY"]
|
10
13
|
)
|
11
14
|
|
12
|
-
|
13
15
|
# Create the default schema.
|
14
16
|
qdrant.create_default_schema
|
15
17
|
|
@@ -33,4 +35,4 @@ qdrant.similarity_search(
|
|
33
35
|
# Interact with your index through Q&A
|
34
36
|
qdrant.ask(
|
35
37
|
question: "What is the best recipe for chicken?"
|
36
|
-
)
|
38
|
+
)
|
@@ -1,5 +1,8 @@
|
|
1
1
|
require "langchain"
|
2
2
|
|
3
|
+
# gem install weaviate-ruby
|
4
|
+
# or add `gem "weaviate-ruby"` to your Gemfile
|
5
|
+
|
3
6
|
# Instantiate the Weaviate client
|
4
7
|
weaviate = Vectorsearch::Weaviate.new(
|
5
8
|
url: ENV["WEAVIATE_URL"],
|
@@ -27,4 +30,4 @@ weaviate.add_texts(
|
|
27
30
|
weaviate.similarity_search(
|
28
31
|
query: "chicken",
|
29
32
|
k: 1
|
30
|
-
)
|
33
|
+
)
|
@@ -5,7 +5,7 @@ module Agent
|
|
5
5
|
attr_reader :llm, :llm_api_key, :llm_client, :tools
|
6
6
|
|
7
7
|
# Initializes the Agent
|
8
|
-
#
|
8
|
+
#
|
9
9
|
# @param llm [Symbol] The LLM to use
|
10
10
|
# @param llm_api_key [String] The API key for the LLM
|
11
11
|
# @param tools [Array] The tools to use
|
@@ -22,7 +22,7 @@ module Agent
|
|
22
22
|
end
|
23
23
|
|
24
24
|
# Validate tools when they're re-assigned
|
25
|
-
#
|
25
|
+
#
|
26
26
|
# @param value [Array] The tools to use
|
27
27
|
# @return [Array] The tools that will be used
|
28
28
|
def tools=(value)
|
@@ -31,11 +31,10 @@ module Agent
|
|
31
31
|
end
|
32
32
|
|
33
33
|
# Run the Agent!
|
34
|
-
#
|
34
|
+
#
|
35
35
|
# @param question [String] The question to ask
|
36
|
-
# @param logging [Boolean] Whether or not to log the Agent's actions
|
37
36
|
# @return [String] The answer to the question
|
38
|
-
def run(question
|
37
|
+
def run(question:)
|
39
38
|
question = question.strip
|
40
39
|
prompt = create_prompt(
|
41
40
|
question: question,
|
@@ -43,7 +42,7 @@ module Agent
|
|
43
42
|
)
|
44
43
|
|
45
44
|
loop do
|
46
|
-
|
45
|
+
Langchain.logger.info("Agent: Passing the prompt to the #{llm} LLM")
|
47
46
|
response = llm_client.generate_completion(
|
48
47
|
prompt: prompt,
|
49
48
|
stop_sequences: ["Observation:"],
|
@@ -51,16 +50,16 @@ module Agent
|
|
51
50
|
)
|
52
51
|
|
53
52
|
# Append the response to the prompt
|
54
|
-
prompt += response
|
55
|
-
|
53
|
+
prompt += response
|
54
|
+
|
56
55
|
# Find the requested action in the "Action: search" format
|
57
56
|
action = response.match(/Action: (.*)/)&.send(:[], -1)
|
58
|
-
|
57
|
+
|
59
58
|
if action
|
60
59
|
# Find the input to the action in the "Action Input: [action_input]" format
|
61
60
|
action_input = response.match(/Action Input: "?(.*)"?/)&.send(:[], -1)
|
62
61
|
|
63
|
-
|
62
|
+
Langchain.logger.info("Agent: Using the \"#{action}\" Tool with \"#{action_input}\"")
|
64
63
|
|
65
64
|
# Retrieve the Tool::[ToolName] class and call `execute`` with action_input as the input
|
66
65
|
result = Tool
|
@@ -68,10 +67,10 @@ module Agent
|
|
68
67
|
.execute(input: action_input)
|
69
68
|
|
70
69
|
# Append the Observation to the prompt
|
71
|
-
if prompt.end_with?("Observation:")
|
72
|
-
|
70
|
+
prompt += if prompt.end_with?("Observation:")
|
71
|
+
" #{result}\nThought:"
|
73
72
|
else
|
74
|
-
|
73
|
+
"\nObservation: #{result}\nThought:"
|
75
74
|
end
|
76
75
|
else
|
77
76
|
# Return the final answer
|
@@ -92,7 +91,7 @@ module Agent
|
|
92
91
|
question: question,
|
93
92
|
tool_names: "[#{tools.join(", ")}]",
|
94
93
|
tools: tools.map do |tool|
|
95
|
-
"#{tool}: #{Tool.const_get(Tool::Base::TOOLS[tool]).const_get(
|
94
|
+
"#{tool}: #{Tool.const_get(Tool::Base::TOOLS[tool]).const_get(:DESCRIPTION)}"
|
96
95
|
end.join("\n")
|
97
96
|
)
|
98
97
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
def depends_on(gem_name)
|
4
|
+
gem(gem_name) # require the gem
|
5
|
+
|
6
|
+
return(true) unless defined?(Bundler) # If we're in a non-bundler environment, we're no longer able to determine if we'll meet requirements
|
7
|
+
|
8
|
+
gem_version = Gem.loaded_specs[gem_name].version
|
9
|
+
gem_requirement = Bundler.load.dependencies.find { |g| g.name == gem_name }.requirement
|
10
|
+
|
11
|
+
if !gem_requirement.satisfied_by?(gem_version)
|
12
|
+
raise "The #{gem_name} gem is installed, but version #{gem_requirement} is required. You have #{gem_version}."
|
13
|
+
end
|
14
|
+
|
15
|
+
true
|
16
|
+
rescue LoadError
|
17
|
+
raise LoadError, "Could not load #{gem_name}. Please ensure that the #{gem_name} gem is installed."
|
18
|
+
end
|
data/lib/langchain.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "./version"
|
4
|
+
require_relative "./dependency_helper"
|
5
|
+
require_relative "./logging"
|
4
6
|
|
5
7
|
module Agent
|
6
8
|
autoload :Base, "agent/base"
|
@@ -18,6 +20,7 @@ end
|
|
18
20
|
module LLM
|
19
21
|
autoload :Base, "llm/base"
|
20
22
|
autoload :Cohere, "llm/cohere"
|
23
|
+
autoload :HuggingFace, "llm/hugging_face"
|
21
24
|
autoload :OpenAI, "llm/openai"
|
22
25
|
end
|
23
26
|
|
data/lib/llm/base.rb
CHANGED
@@ -12,16 +12,16 @@ module LLM
|
|
12
12
|
}.freeze
|
13
13
|
|
14
14
|
def default_dimension
|
15
|
-
self.class.const_get(
|
15
|
+
self.class.const_get(:DEFAULTS).dig(:dimension)
|
16
16
|
end
|
17
17
|
|
18
18
|
# Ensure that the LLM value passed in is supported
|
19
19
|
# @param llm [Symbol] The LLM to use
|
20
20
|
def self.validate_llm!(llm:)
|
21
21
|
# TODO: Fix so this works when `llm` value is a string instead of a symbol
|
22
|
-
unless LLM::Base::LLMS.
|
22
|
+
unless LLM::Base::LLMS.key?(llm)
|
23
23
|
raise ArgumentError, "LLM must be one of #{LLM::Base::LLMS.keys}"
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
27
|
-
end
|
27
|
+
end
|
data/lib/llm/cohere.rb
CHANGED
@@ -1,10 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "cohere"
|
4
|
-
|
5
3
|
module LLM
|
6
4
|
class Cohere < Base
|
7
|
-
|
8
5
|
DEFAULTS = {
|
9
6
|
temperature: 0.0,
|
10
7
|
completion_model_name: "base",
|
@@ -13,6 +10,9 @@ module LLM
|
|
13
10
|
}.freeze
|
14
11
|
|
15
12
|
def initialize(api_key:)
|
13
|
+
depends_on "cohere-ruby"
|
14
|
+
require "cohere"
|
15
|
+
|
16
16
|
@client = ::Cohere::Client.new(api_key: api_key)
|
17
17
|
end
|
18
18
|
|
@@ -22,7 +22,7 @@ module LLM
|
|
22
22
|
def embed(text:)
|
23
23
|
response = client.embed(
|
24
24
|
texts: [text],
|
25
|
-
model: DEFAULTS[:embeddings_model_name]
|
25
|
+
model: DEFAULTS[:embeddings_model_name]
|
26
26
|
)
|
27
27
|
response.dig("embeddings").first
|
28
28
|
end
|
@@ -50,4 +50,4 @@ module LLM
|
|
50
50
|
alias_method :generate_completion, :complete
|
51
51
|
alias_method :generate_embedding, :embed
|
52
52
|
end
|
53
|
-
end
|
53
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LLM
|
4
|
+
class HuggingFace < Base
|
5
|
+
# The gem does not currently accept other models:
|
6
|
+
# https://github.com/alchaplinsky/hugging-face/blob/main/lib/hugging_face/inference_api.rb#L32-L34
|
7
|
+
DEFAULTS = {
|
8
|
+
embeddings_model_name: "sentence-transformers/all-MiniLM-L6-v2"
|
9
|
+
}.freeze
|
10
|
+
|
11
|
+
#
|
12
|
+
# Intialize the HuggingFace LLM
|
13
|
+
# @param api_key [String] The API key to use
|
14
|
+
#
|
15
|
+
def initialize(api_key:)
|
16
|
+
depends_on "hugging-face"
|
17
|
+
require "hugging_face"
|
18
|
+
|
19
|
+
@client = ::HuggingFace::InferenceApi.new(api_token: api_key)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Generate an embedding for a given text
|
23
|
+
# @param text [String] The text to embed
|
24
|
+
# @return [Array] The embedding
|
25
|
+
def embed(text:)
|
26
|
+
response = client.embedding(
|
27
|
+
input: text,
|
28
|
+
model: DEFAULTS[:embeddings_model_name]
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/llm/openai.rb
CHANGED
@@ -1,10 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "openai"
|
4
|
-
|
5
3
|
module LLM
|
6
4
|
class OpenAI < Base
|
7
|
-
|
8
5
|
DEFAULTS = {
|
9
6
|
temperature: 0.0,
|
10
7
|
completion_model_name: "text-davinci-003",
|
@@ -13,6 +10,9 @@ module LLM
|
|
13
10
|
}.freeze
|
14
11
|
|
15
12
|
def initialize(api_key:)
|
13
|
+
depends_on "ruby-openai"
|
14
|
+
require "openai"
|
15
|
+
|
16
16
|
# TODO: Add support to pass `organization_id:`
|
17
17
|
@client = ::OpenAI::Client.new(access_token: api_key)
|
18
18
|
end
|
@@ -53,4 +53,4 @@ module LLM
|
|
53
53
|
alias_method :generate_completion, :complete
|
54
54
|
alias_method :generate_embedding, :embed
|
55
55
|
end
|
56
|
-
end
|
56
|
+
end
|
data/lib/logging.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "logger"
|
4
|
+
|
5
|
+
module Langchain
|
6
|
+
def self.logger
|
7
|
+
@@logger ||= Logger.new($stdout, level: :warn, formatter: ->(severity, datetime, progname, msg) { "[LangChain.rb] #{msg}\n" })
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.logger=(instance)
|
11
|
+
@@logger = instance
|
12
|
+
end
|
13
|
+
end
|
data/lib/prompt/base.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "strscan"
|
4
|
+
require "json"
|
4
5
|
|
5
6
|
module Prompt
|
6
7
|
class Base
|
@@ -52,14 +53,12 @@ module Prompt
|
|
52
53
|
FileUtils.mkdir_p(directory_path) unless directory_path.directory?
|
53
54
|
|
54
55
|
if save_path.extname == ".json"
|
55
|
-
File.
|
56
|
+
File.write(file_path, to_h.to_json)
|
56
57
|
else
|
57
58
|
raise ArgumentError, "#{file_path} must be json"
|
58
59
|
end
|
59
60
|
end
|
60
61
|
|
61
|
-
private
|
62
|
-
|
63
62
|
#
|
64
63
|
# Extracts variables from a template string.
|
65
64
|
#
|
data/lib/prompt/loading.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "strscan"
|
4
|
+
require "pathname"
|
5
5
|
|
6
6
|
module Prompt
|
7
7
|
TYPE_TO_LOADER = {
|
@@ -70,7 +70,7 @@ module Prompt
|
|
70
70
|
def load_from_config(config)
|
71
71
|
# If `_type` key is not present in the configuration hash, add it with a default value of `prompt`
|
72
72
|
unless config.key?("_type")
|
73
|
-
|
73
|
+
Langchain.logger.warn "No `_type` key found, defaulting to `prompt`"
|
74
74
|
config["_type"] = "prompt"
|
75
75
|
end
|
76
76
|
|