langchainrb 0.3.2 → 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +46 -2
- data/README.md +13 -1
- data/Rakefile +5 -0
- data/examples/create_and_manage_few_shot_prompt_templates.rb +3 -3
- data/examples/store_and_query_with_pinecone.rb +2 -2
- data/examples/store_and_query_with_qdrant.rb +1 -2
- data/examples/store_and_query_with_weaviate.rb +1 -1
- data/lib/agent/chain_of_thought_agent/chain_of_thought_agent.rb +14 -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/openai.rb +1 -2
- data/lib/logging.rb +13 -0
- data/lib/prompt/base.rb +2 -4
- data/lib/prompt/loading.rb +3 -3
- data/lib/tool/base.rb +11 -6
- data/lib/tool/calculator.rb +5 -2
- data/lib/tool/serp_api.rb +10 -6
- data/lib/tool/wikipedia.rb +10 -6
- data/lib/vectorsearch/base.rb +3 -3
- data/lib/vectorsearch/milvus.rb +2 -5
- data/lib/vectorsearch/pinecone.rb +4 -4
- data/lib/vectorsearch/qdrant.rb +3 -3
- data/lib/vectorsearch/weaviate.rb +3 -3
- data/lib/version.rb +1 -1
- metadata +11 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 735fb9932ea28c3d6f3938b964e4edd5d030ed4c12c46a9f35293d35469cf952
|
4
|
+
data.tar.gz: 66a8030ea90c19c1950c4136c48677b6490df948dc2693562eca7b8a370dc3ae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 426ae91fc1c2d297b84758f6fe87d62a6dbaf4614776ab7d247c994ce849a979d1752d1241963f7fd3b8d71a68dc6f576660daabcacf19159b63acdda65fa7d9
|
7
|
+
data.tar.gz: 4acd1a02be734cccc431499c0e1659694004f8ee30b27b99d51b580373c4a410e87f8bf96edfeec38e67f1066d34441ccbb4ce6e4b6597623f606876e3fcb16f
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
langchainrb (0.3.
|
5
|
-
cohere-ruby (~> 0.9.3)
|
4
|
+
langchainrb (0.3.3)
|
6
5
|
eqn (~> 1.6.5)
|
7
6
|
google_search_results (~> 2.0.0)
|
8
7
|
milvus (~> 0.9.0)
|
@@ -35,6 +34,7 @@ GEM
|
|
35
34
|
tzinfo (~> 2.0)
|
36
35
|
addressable (2.8.4)
|
37
36
|
public_suffix (>= 2.0.2, < 6.0)
|
37
|
+
ast (2.4.2)
|
38
38
|
builder (3.2.4)
|
39
39
|
byebug (11.1.3)
|
40
40
|
coderay (1.1.3)
|
@@ -128,6 +128,9 @@ GEM
|
|
128
128
|
i18n (1.13.0)
|
129
129
|
concurrent-ruby (~> 1.0)
|
130
130
|
ice_nine (0.11.2)
|
131
|
+
json (2.6.3)
|
132
|
+
language_server-protocol (3.17.0.3)
|
133
|
+
lint_roller (1.0.0)
|
131
134
|
loofah (2.21.1)
|
132
135
|
crass (~> 1.0.2)
|
133
136
|
nokogiri (>= 1.5.9)
|
@@ -138,10 +141,15 @@ GEM
|
|
138
141
|
minitest (5.18.0)
|
139
142
|
multi_xml (0.6.0)
|
140
143
|
multipart-post (2.3.0)
|
144
|
+
nokogiri (1.14.3-arm64-darwin)
|
145
|
+
racc (~> 1.4)
|
141
146
|
nokogiri (1.14.3-x86_64-darwin)
|
142
147
|
racc (~> 1.4)
|
143
148
|
nokogiri (1.14.3-x86_64-linux)
|
144
149
|
racc (~> 1.4)
|
150
|
+
parallel (1.23.0)
|
151
|
+
parser (3.2.2.1)
|
152
|
+
ast (~> 2.4.1)
|
145
153
|
pinecone (0.1.71)
|
146
154
|
dry-struct (~> 1.6.0)
|
147
155
|
dry-validation (~> 1.10.0)
|
@@ -173,7 +181,10 @@ GEM
|
|
173
181
|
rake (>= 12.2)
|
174
182
|
thor (~> 1.0)
|
175
183
|
zeitwerk (~> 2.5)
|
184
|
+
rainbow (3.1.1)
|
176
185
|
rake (13.0.6)
|
186
|
+
regexp_parser (2.8.0)
|
187
|
+
rexml (3.2.5)
|
177
188
|
rspec (3.12.0)
|
178
189
|
rspec-core (~> 3.12.0)
|
179
190
|
rspec-expectations (~> 3.12.0)
|
@@ -187,15 +198,45 @@ GEM
|
|
187
198
|
diff-lcs (>= 1.2.0, < 2.0)
|
188
199
|
rspec-support (~> 3.12.0)
|
189
200
|
rspec-support (3.12.0)
|
201
|
+
rubocop (1.50.2)
|
202
|
+
json (~> 2.3)
|
203
|
+
parallel (~> 1.10)
|
204
|
+
parser (>= 3.2.0.0)
|
205
|
+
rainbow (>= 2.2.2, < 4.0)
|
206
|
+
regexp_parser (>= 1.8, < 3.0)
|
207
|
+
rexml (>= 3.2.5, < 4.0)
|
208
|
+
rubocop-ast (>= 1.28.0, < 2.0)
|
209
|
+
ruby-progressbar (~> 1.7)
|
210
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
211
|
+
rubocop-ast (1.28.1)
|
212
|
+
parser (>= 3.2.1.0)
|
213
|
+
rubocop-performance (1.16.0)
|
214
|
+
rubocop (>= 1.7.0, < 2.0)
|
215
|
+
rubocop-ast (>= 0.4.0)
|
190
216
|
ruby-openai (4.0.0)
|
191
217
|
faraday (>= 1)
|
192
218
|
faraday-multipart (>= 1)
|
219
|
+
ruby-progressbar (1.13.0)
|
193
220
|
ruby2_keywords (0.0.5)
|
221
|
+
standard (1.28.2)
|
222
|
+
language_server-protocol (~> 3.17.0.2)
|
223
|
+
lint_roller (~> 1.0)
|
224
|
+
rubocop (~> 1.50.2)
|
225
|
+
standard-custom (~> 1.0.0)
|
226
|
+
standard-performance (~> 1.0.1)
|
227
|
+
standard-custom (1.0.0)
|
228
|
+
lint_roller (~> 1.0)
|
229
|
+
standard-performance (1.0.1)
|
230
|
+
lint_roller (~> 1.0)
|
231
|
+
rubocop-performance (~> 1.16.0)
|
232
|
+
standardrb (1.0.1)
|
233
|
+
standard
|
194
234
|
thor (1.2.1)
|
195
235
|
treetop (1.6.12)
|
196
236
|
polyglot (~> 0.3)
|
197
237
|
tzinfo (2.0.6)
|
198
238
|
concurrent-ruby (~> 1.0)
|
239
|
+
unicode-display_width (2.4.2)
|
199
240
|
weaviate-ruby (0.8.1)
|
200
241
|
faraday (~> 1)
|
201
242
|
faraday_middleware (~> 1)
|
@@ -205,15 +246,18 @@ GEM
|
|
205
246
|
zeitwerk (2.6.8)
|
206
247
|
|
207
248
|
PLATFORMS
|
249
|
+
arm64-darwin-22
|
208
250
|
x86_64-darwin-19
|
209
251
|
x86_64-linux
|
210
252
|
|
211
253
|
DEPENDENCIES
|
254
|
+
cohere-ruby (~> 0.9.3)
|
212
255
|
dotenv-rails (~> 2.7.6)
|
213
256
|
langchainrb!
|
214
257
|
pry-byebug (~> 3.10.0)
|
215
258
|
rake (~> 13.0)
|
216
259
|
rspec (~> 3.0)
|
260
|
+
standardrb
|
217
261
|
|
218
262
|
BUNDLED WITH
|
219
263
|
2.4.0
|
data/README.md
CHANGED
@@ -104,6 +104,8 @@ openai.complete(prompt: "What is the meaning of life?")
|
|
104
104
|
```
|
105
105
|
|
106
106
|
#### Cohere
|
107
|
+
Add `gem "cohere-ruby", "~> 0.9.3"` to your Gemfile.
|
108
|
+
|
107
109
|
```ruby
|
108
110
|
cohere = LLM::Cohere.new(api_key: ENV["COHERE_API_KEY"])
|
109
111
|
```
|
@@ -211,7 +213,7 @@ agent.tools
|
|
211
213
|
# => ["search", "calculator"]
|
212
214
|
```
|
213
215
|
```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?"
|
216
|
+
agent.run(question: "How many full soccer fields would be needed to cover the distance between NYC and DC in a straight line?")
|
215
217
|
#=> "Approximately 2,945 soccer fields would be needed to cover the distance between NYC and DC in a straight line."
|
216
218
|
```
|
217
219
|
|
@@ -228,6 +230,16 @@ agent.run(question: "How many full soccer fields would be needed to cover the di
|
|
228
230
|
| "search" | A wrapper around Google Search | `ENV["SERPAPI_API_KEY"]` (https://serpapi.com/manage-api-key)
|
229
231
|
| "wikipedia" | Calls Wikipedia API to retrieve the summary | |
|
230
232
|
|
233
|
+
|
234
|
+
## Logging
|
235
|
+
|
236
|
+
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.
|
237
|
+
To show all log messages:
|
238
|
+
|
239
|
+
```ruby
|
240
|
+
Lanchgain.logger.level = :info
|
241
|
+
```
|
242
|
+
|
231
243
|
## Development
|
232
244
|
|
233
245
|
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")
|
@@ -35,9 +35,9 @@ pinecone.ask(
|
|
35
35
|
)
|
36
36
|
|
37
37
|
# Generate your an embedding and search by it
|
38
|
-
openai = LLM::OpenAI.new(api_key: ENV[
|
38
|
+
openai = LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
|
39
39
|
embedding = openai.embed(text: "veggie")
|
40
40
|
|
41
41
|
pinecone.similarity_search_by_vector(
|
42
42
|
embedding: embedding
|
43
|
-
)
|
43
|
+
)
|
@@ -9,7 +9,6 @@ qdrant = Vectorsearch::Qdrant.new(
|
|
9
9
|
llm_api_key: ENV["COHERE_API_KEY"]
|
10
10
|
)
|
11
11
|
|
12
|
-
|
13
12
|
# Create the default schema.
|
14
13
|
qdrant.create_default_schema
|
15
14
|
|
@@ -33,4 +32,4 @@ qdrant.similarity_search(
|
|
33
32
|
# Interact with your index through Q&A
|
34
33
|
qdrant.ask(
|
35
34
|
question: "What is the best recipe for chicken?"
|
36
|
-
)
|
35
|
+
)
|
@@ -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,24 +42,25 @@ 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:"],
|
50
49
|
max_tokens: 500
|
51
50
|
)
|
52
51
|
|
52
|
+
binding.pry
|
53
53
|
# Append the response to the prompt
|
54
|
-
prompt += response
|
55
|
-
|
54
|
+
prompt += response
|
55
|
+
|
56
56
|
# Find the requested action in the "Action: search" format
|
57
57
|
action = response.match(/Action: (.*)/)&.send(:[], -1)
|
58
|
-
|
58
|
+
|
59
59
|
if action
|
60
60
|
# Find the input to the action in the "Action Input: [action_input]" format
|
61
61
|
action_input = response.match(/Action Input: "?(.*)"?/)&.send(:[], -1)
|
62
62
|
|
63
|
-
|
63
|
+
Langchain.logger.info("Agent: Using the \"#{action}\" Tool with \"#{action_input}\"")
|
64
64
|
|
65
65
|
# Retrieve the Tool::[ToolName] class and call `execute`` with action_input as the input
|
66
66
|
result = Tool
|
@@ -68,10 +68,10 @@ module Agent
|
|
68
68
|
.execute(input: action_input)
|
69
69
|
|
70
70
|
# Append the Observation to the prompt
|
71
|
-
if prompt.end_with?("Observation:")
|
72
|
-
|
71
|
+
prompt += if prompt.end_with?("Observation:")
|
72
|
+
" #{result}\nThought:"
|
73
73
|
else
|
74
|
-
|
74
|
+
"\nObservation: #{result}\nThought:"
|
75
75
|
end
|
76
76
|
else
|
77
77
|
# Return the final answer
|
@@ -92,7 +92,7 @@ module Agent
|
|
92
92
|
question: question,
|
93
93
|
tool_names: "[#{tools.join(", ")}]",
|
94
94
|
tools: tools.map do |tool|
|
95
|
-
"#{tool}: #{Tool.const_get(Tool::Base::TOOLS[tool]).const_get(
|
95
|
+
"#{tool}: #{Tool.const_get(Tool::Base::TOOLS[tool]).const_get(:DESCRIPTION)}"
|
96
96
|
end.join("\n")
|
97
97
|
)
|
98
98
|
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"
|
@@ -32,6 +34,7 @@ end
|
|
32
34
|
module Tool
|
33
35
|
autoload :Base, "tool/base"
|
34
36
|
autoload :Calculator, "tool/calculator"
|
37
|
+
autoload :News, "tool/news"
|
35
38
|
autoload :SerpApi, "tool/serp_api"
|
36
39
|
autoload :Wikipedia, "tool/wikipedia"
|
37
40
|
end
|
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
|
data/lib/llm/openai.rb
CHANGED
@@ -4,7 +4,6 @@ require "openai"
|
|
4
4
|
|
5
5
|
module LLM
|
6
6
|
class OpenAI < Base
|
7
|
-
|
8
7
|
DEFAULTS = {
|
9
8
|
temperature: 0.0,
|
10
9
|
completion_model_name: "text-davinci-003",
|
@@ -53,4 +52,4 @@ module LLM
|
|
53
52
|
alias_method :generate_completion, :complete
|
54
53
|
alias_method :generate_embedding, :embed
|
55
54
|
end
|
56
|
-
end
|
55
|
+
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,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "strscan"
|
4
4
|
|
5
5
|
module Prompt
|
6
6
|
class Base
|
@@ -52,14 +52,12 @@ module Prompt
|
|
52
52
|
FileUtils.mkdir_p(directory_path) unless directory_path.directory?
|
53
53
|
|
54
54
|
if save_path.extname == ".json"
|
55
|
-
File.
|
55
|
+
File.write(file_path, to_h.to_json)
|
56
56
|
else
|
57
57
|
raise ArgumentError, "#{file_path} must be json"
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
-
private
|
62
|
-
|
63
61
|
#
|
64
62
|
# Extracts variables from a template string.
|
65
63
|
#
|
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
|
|
data/lib/tool/base.rb
CHANGED
@@ -12,9 +12,14 @@ module Tool
|
|
12
12
|
TOOLS = {
|
13
13
|
"calculator" => "Tool::Calculator",
|
14
14
|
"search" => "Tool::SerpApi",
|
15
|
-
"wikipedia" => "Tool::Wikipedia"
|
15
|
+
"wikipedia" => "Tool::Wikipedia",
|
16
|
+
"news" => "Tool::News"
|
16
17
|
}
|
17
18
|
|
19
|
+
def self.description(value)
|
20
|
+
const_set(:DESCRIPTION, value.tr("\n", " ").strip)
|
21
|
+
end
|
22
|
+
|
18
23
|
# Executes the tool and returns the answer
|
19
24
|
# @param input [String] input to the tool
|
20
25
|
# @return [String] answer
|
@@ -22,17 +27,17 @@ module Tool
|
|
22
27
|
raise NotImplementedError, "Your tool must implement the `self.execute(input:)` method that returns a string"
|
23
28
|
end
|
24
29
|
|
25
|
-
#
|
30
|
+
#
|
26
31
|
# Validates the list of strings (tools) are all supported or raises an error
|
27
32
|
# @param tools [Array<String>] list of tools to be used
|
28
|
-
#
|
33
|
+
#
|
29
34
|
# @raise [ArgumentError] If any of the tools are not supported
|
30
|
-
#
|
35
|
+
#
|
31
36
|
def self.validate_tools!(tools:)
|
32
|
-
unrecognized_tools = tools - Tool::Base::TOOLS.keys
|
37
|
+
unrecognized_tools = tools - Tool::Base::TOOLS.keys
|
33
38
|
|
34
39
|
if unrecognized_tools.any?
|
35
|
-
raise ArgumentError, "Unrecognized Tools: #{unrecognized_tools}"
|
40
|
+
raise ArgumentError, "Unrecognized Tools: #{unrecognized_tools}"
|
36
41
|
end
|
37
42
|
end
|
38
43
|
end
|
data/lib/tool/calculator.rb
CHANGED
@@ -4,8 +4,11 @@ require "eqn"
|
|
4
4
|
|
5
5
|
module Tool
|
6
6
|
class Calculator < Base
|
7
|
-
|
8
|
-
|
7
|
+
description <<~DESC
|
8
|
+
Useful for getting the result of a math expression.
|
9
|
+
|
10
|
+
The input to this tool should be a valid mathematical expression that could be executed by a simple calculator.
|
11
|
+
DESC
|
9
12
|
|
10
13
|
# Evaluates a pure math expression or if equation contains non-math characters (e.g.: "12F in Celsius") then
|
11
14
|
# it uses the google search calculator to evaluate the expression
|
data/lib/tool/serp_api.rb
CHANGED
@@ -7,10 +7,14 @@ module Tool
|
|
7
7
|
# Wrapper around SerpAPI
|
8
8
|
# Set ENV["SERPAPI_API_KEY"] to use it
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
description <<~DESC
|
11
|
+
A wrapper around Google Search.
|
12
|
+
|
13
|
+
Useful for when you need to answer questions about current events.
|
14
|
+
Always one of the first options when you need to find information on internet.
|
15
|
+
|
16
|
+
Input should be a search query.
|
17
|
+
DESC
|
14
18
|
|
15
19
|
# Executes Google Search and returns hash_results JSON
|
16
20
|
# @param input [String] search query
|
@@ -18,7 +22,7 @@ module Tool
|
|
18
22
|
# TODO: Glance at all of the fields that langchain Python looks through: https://github.com/hwchase17/langchain/blob/v0.0.166/langchain/utilities/serpapi.py#L128-L156
|
19
23
|
# We may need to do the same thing here.
|
20
24
|
def self.execute(input:)
|
21
|
-
hash_results =
|
25
|
+
hash_results = execute_search(input: input)
|
22
26
|
|
23
27
|
hash_results.dig(:answer_box, :answer) ||
|
24
28
|
hash_results.dig(:answer_box, :snippet) ||
|
@@ -33,7 +37,7 @@ module Tool
|
|
33
37
|
q: input,
|
34
38
|
serp_api_key: ENV["SERPAPI_API_KEY"]
|
35
39
|
)
|
36
|
-
|
40
|
+
.get_hash
|
37
41
|
end
|
38
42
|
end
|
39
43
|
end
|
data/lib/tool/wikipedia.rb
CHANGED
@@ -1,15 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "wikipedia"
|
4
4
|
|
5
5
|
module Tool
|
6
6
|
class Wikipedia < Base
|
7
7
|
# Tool that adds the capability to search using the Wikipedia API
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
description <<~DESC
|
10
|
+
A wrapper around Wikipedia.
|
11
|
+
|
12
|
+
Useful for when you need to answer general questions about
|
13
|
+
people, places, companies, facts, historical events, or other subjects.
|
14
|
+
|
15
|
+
Input should be a search query.
|
16
|
+
DESC
|
13
17
|
|
14
18
|
# Executes Wikipedia API search and returns the answer
|
15
19
|
# @param input [String] search query
|
@@ -17,7 +21,7 @@ module Tool
|
|
17
21
|
def self.execute(input:)
|
18
22
|
page = ::Wikipedia.find(input)
|
19
23
|
# It would be nice to figure out a way to provide page.content but the LLM token limit is an issue
|
20
|
-
page.summary
|
24
|
+
page.summary
|
21
25
|
end
|
22
26
|
end
|
23
27
|
end
|
data/lib/vectorsearch/base.rb
CHANGED
@@ -6,7 +6,7 @@ module Vectorsearch
|
|
6
6
|
|
7
7
|
attr_reader :client, :index_name, :llm, :llm_api_key, :llm_client
|
8
8
|
|
9
|
-
DEFAULT_METRIC = "cosine"
|
9
|
+
DEFAULT_METRIC = "cosine"
|
10
10
|
|
11
11
|
# @param llm [Symbol] The LLM to use
|
12
12
|
# @param llm_api_key [String] The API key for the LLM
|
@@ -46,7 +46,7 @@ module Vectorsearch
|
|
46
46
|
input_variables: ["context"]
|
47
47
|
),
|
48
48
|
examples: [
|
49
|
-
{
|
49
|
+
{context: context}
|
50
50
|
],
|
51
51
|
input_variables: ["question"],
|
52
52
|
example_separator: "\n"
|
@@ -55,4 +55,4 @@ module Vectorsearch
|
|
55
55
|
prompt_template.format(question: question)
|
56
56
|
end
|
57
57
|
end
|
58
|
-
end
|
58
|
+
end
|
data/lib/vectorsearch/milvus.rb
CHANGED
@@ -6,10 +6,7 @@ module Vectorsearch
|
|
6
6
|
class Milvus < Base
|
7
7
|
def initialize(
|
8
8
|
url:,
|
9
|
-
api_key: nil
|
10
|
-
index_name:,
|
11
|
-
llm:,
|
12
|
-
llm_api_key:
|
9
|
+
index_name:, llm:, llm_api_key:, api_key: nil
|
13
10
|
)
|
14
11
|
@client = ::Milvus::Client.new(
|
15
12
|
url: url
|
@@ -96,7 +93,7 @@ module Vectorsearch
|
|
96
93
|
client.search(
|
97
94
|
collection_name: index_name,
|
98
95
|
top_k: k.to_s,
|
99
|
-
vectors: [
|
96
|
+
vectors: [embedding],
|
100
97
|
dsl_type: 1,
|
101
98
|
params: "{\"nprobe\": 10}",
|
102
99
|
anns_field: "content",
|
@@ -18,10 +18,10 @@ module Vectorsearch
|
|
18
18
|
llm_api_key:
|
19
19
|
)
|
20
20
|
::Pinecone.configure do |config|
|
21
|
-
config.api_key
|
21
|
+
config.api_key = api_key
|
22
22
|
config.environment = environment
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
@client = ::Pinecone::Client.new
|
26
26
|
@index_name = index_name
|
27
27
|
|
@@ -38,7 +38,7 @@ module Vectorsearch
|
|
38
38
|
{
|
39
39
|
# TODO: Allows passing in your own IDs
|
40
40
|
id: SecureRandom.uuid,
|
41
|
-
metadata: {
|
41
|
+
metadata: {content: text},
|
42
42
|
values: generate_embedding(text: text)
|
43
43
|
}
|
44
44
|
end
|
@@ -109,4 +109,4 @@ module Vectorsearch
|
|
109
109
|
generate_completion(prompt: prompt)
|
110
110
|
end
|
111
111
|
end
|
112
|
-
end
|
112
|
+
end
|
data/lib/vectorsearch/qdrant.rb
CHANGED
@@ -32,12 +32,12 @@ module Vectorsearch
|
|
32
32
|
def add_texts(
|
33
33
|
texts:
|
34
34
|
)
|
35
|
-
batch = {
|
35
|
+
batch = {ids: [], vectors: [], payloads: []}
|
36
36
|
|
37
37
|
texts.each do |text|
|
38
38
|
batch[:ids].push(SecureRandom.uuid)
|
39
39
|
batch[:vectors].push(generate_embedding(text: text))
|
40
|
-
batch[:payloads].push({
|
40
|
+
batch[:payloads].push({content: text})
|
41
41
|
end
|
42
42
|
|
43
43
|
client.points.upsert(
|
@@ -106,4 +106,4 @@ module Vectorsearch
|
|
106
106
|
generate_completion(prompt: prompt)
|
107
107
|
end
|
108
108
|
end
|
109
|
-
end
|
109
|
+
end
|
@@ -37,7 +37,7 @@ module Vectorsearch
|
|
37
37
|
objects = texts.map do |text|
|
38
38
|
{
|
39
39
|
class: index_name,
|
40
|
-
properties: {
|
40
|
+
properties: {content: text}
|
41
41
|
}
|
42
42
|
end
|
43
43
|
|
@@ -50,7 +50,7 @@ module Vectorsearch
|
|
50
50
|
def create_default_schema
|
51
51
|
client.schema.create(
|
52
52
|
class_name: index_name,
|
53
|
-
vectorizer: "text2vec-#{llm
|
53
|
+
vectorizer: "text2vec-#{llm}",
|
54
54
|
# TODO: Figure out a way to optionally enable it
|
55
55
|
# "module_config": {
|
56
56
|
# "qna-openai": {}
|
@@ -132,4 +132,4 @@ module Vectorsearch
|
|
132
132
|
end
|
133
133
|
end
|
134
134
|
end
|
135
|
-
end
|
135
|
+
end
|
data/lib/version.rb
CHANGED
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: langchainrb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrei Bondarev
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-05-
|
11
|
+
date: 2023-05-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: cohere-ruby
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 0.9.3
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 0.9.3
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: dotenv-rails
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -39,19 +39,19 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 2.7.6
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: pry-byebug
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
48
|
-
type: :
|
47
|
+
version: 3.10.0
|
48
|
+
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 3.10.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: eqn
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -187,10 +187,12 @@ files:
|
|
187
187
|
- lib/agent/base.rb
|
188
188
|
- lib/agent/chain_of_thought_agent/chain_of_thought_agent.rb
|
189
189
|
- lib/agent/chain_of_thought_agent/chain_of_thought_agent_prompt.json
|
190
|
+
- lib/dependency_helper.rb
|
190
191
|
- lib/langchain.rb
|
191
192
|
- lib/llm/base.rb
|
192
193
|
- lib/llm/cohere.rb
|
193
194
|
- lib/llm/openai.rb
|
195
|
+
- lib/logging.rb
|
194
196
|
- lib/prompt/base.rb
|
195
197
|
- lib/prompt/few_shot_prompt_template.rb
|
196
198
|
- lib/prompt/loading.rb
|