langchainrb 0.3.2 → 0.3.3
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 +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
|