langchainrb 0.5.1 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/Gemfile.lock +1 -1
- data/README.md +2 -3
- data/examples/create_and_manage_few_shot_prompt_templates.rb +3 -3
- data/examples/create_and_manage_prompt_templates.rb +8 -4
- data/examples/pdf_store_and_query_with_chroma.rb +1 -1
- data/examples/store_and_query_with_pinecone.rb +2 -2
- data/examples/store_and_query_with_qdrant.rb +1 -1
- data/examples/store_and_query_with_weaviate.rb +1 -1
- data/lib/langchain/agent/chain_of_thought_agent/chain_of_thought_agent.rb +2 -5
- data/lib/langchain/agent/sql_query_agent/sql_query_agent.rb +3 -3
- data/lib/langchain/llm/openai.rb +4 -11
- data/lib/langchain/tool/calculator.rb +2 -1
- data/lib/langchain/utils/token_length_validator.rb +35 -8
- data/lib/langchain/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d36de4206b792714ba9b6773c03272e9638b14caf7140e0bc00c3e767aa5fdef
|
4
|
+
data.tar.gz: 819fab9de55a34e4e6dc865febc19bb9979df55fa8fc6a753774cf1961c40103
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6e180b41bbca96bd5523c276923f223bbebe470314086c6a909df440890793bcc70dbd66ecf59bf5d0fd52426650cc5d2684c56cc8fc643209cc1679527cbef4
|
7
|
+
data.tar.gz: af5db76c2b22b5c7bdc1170de437921e8464a16566f46a5cad465d69e6da47c97a82f7331a5ea5747840e58acc71463aa8456b03e9bc8851efda7b734e5d23cc
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -281,11 +281,10 @@ Add `gem "sequel"` to your Gemfile
|
|
281
281
|
```ruby
|
282
282
|
database = Langchain::Tool::Database.new(connection_string: "postgres://user:password@localhost:5432/db_name")
|
283
283
|
|
284
|
-
agent = Langchain::Agent::SQLQueryAgent.new(llm: Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"]),
|
285
|
-
|
284
|
+
agent = Langchain::Agent::SQLQueryAgent.new(llm: Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"]), db: database)
|
286
285
|
```
|
287
286
|
```ruby
|
288
|
-
agent.
|
287
|
+
agent.run(question: "How many users have a name with length greater than 5 in the users table?")
|
289
288
|
#=> "14 users have a name with length greater than 5 in the users table."
|
290
289
|
```
|
291
290
|
|
@@ -1,10 +1,10 @@
|
|
1
1
|
require "langchain"
|
2
2
|
|
3
3
|
# Create a prompt with a few shot examples
|
4
|
-
prompt = Prompt::FewShotPromptTemplate.new(
|
4
|
+
prompt = Langchain::Prompt::FewShotPromptTemplate.new(
|
5
5
|
prefix: "Write antonyms for the following words.",
|
6
6
|
suffix: "Input: {adjective}\nOutput:",
|
7
|
-
example_prompt: Prompt::PromptTemplate.new(
|
7
|
+
example_prompt: Langchain::Prompt::PromptTemplate.new(
|
8
8
|
input_variables: ["input", "output"],
|
9
9
|
template: "Input: {input}\nOutput: {output}"
|
10
10
|
),
|
@@ -32,5 +32,5 @@ prompt.format(adjective: "good")
|
|
32
32
|
prompt.save(file_path: "spec/fixtures/prompt/few_shot_prompt_template.json")
|
33
33
|
|
34
34
|
# Loading a new prompt template using a JSON file
|
35
|
-
prompt = Prompt.load_from_path(file_path: "spec/fixtures/prompt/few_shot_prompt_template.json")
|
35
|
+
prompt = Langchain::Prompt.load_from_path(file_path: "spec/fixtures/prompt/few_shot_prompt_template.json")
|
36
36
|
prompt.prefix # "Write antonyms for the following words."
|
@@ -1,15 +1,15 @@
|
|
1
1
|
require "langchain"
|
2
2
|
|
3
3
|
# Create a prompt with one input variable
|
4
|
-
prompt = Prompt::PromptTemplate.new(template: "Tell me a {adjective} joke.", input_variables: ["adjective"])
|
4
|
+
prompt = Langchain::Prompt::PromptTemplate.new(template: "Tell me a {adjective} joke.", input_variables: ["adjective"])
|
5
5
|
prompt.format(adjective: "funny") # "Tell me a funny joke."
|
6
6
|
|
7
7
|
# Create a prompt with multiple input variables
|
8
|
-
prompt = Prompt::PromptTemplate.new(template: "Tell me a {adjective} joke about {content}.", input_variables: ["adjective", "content"])
|
8
|
+
prompt = Langchain::Prompt::PromptTemplate.new(template: "Tell me a {adjective} joke about {content}.", input_variables: ["adjective", "content"])
|
9
9
|
prompt.format(adjective: "funny", content: "chickens") # "Tell me a funny joke about chickens."
|
10
10
|
|
11
11
|
# Creating a PromptTemplate using just a prompt and no input_variables
|
12
|
-
prompt = Prompt::PromptTemplate.from_template("Tell me a {adjective} joke about {content}.")
|
12
|
+
prompt = Langchain::Prompt::PromptTemplate.from_template("Tell me a {adjective} joke about {content}.")
|
13
13
|
prompt.input_variables # ["adjective", "content"]
|
14
14
|
prompt.format(adjective: "funny", content: "chickens") # "Tell me a funny joke about chickens."
|
15
15
|
|
@@ -17,5 +17,9 @@ prompt.format(adjective: "funny", content: "chickens") # "Tell me a funny joke a
|
|
17
17
|
prompt.save(file_path: "spec/fixtures/prompt/prompt_template.json")
|
18
18
|
|
19
19
|
# Loading a new prompt template using a JSON file
|
20
|
-
prompt = Prompt.load_from_path(file_path: "spec/fixtures/prompt/prompt_template.json")
|
20
|
+
prompt = Langchain::Prompt.load_from_path(file_path: "spec/fixtures/prompt/prompt_template.json")
|
21
|
+
prompt.input_variables # ["adjective", "content"]
|
22
|
+
|
23
|
+
# Loading a new prompt template using a YAML file
|
24
|
+
prompt = Langchain::Prompt.load_from_path(file_path: "spec/fixtures/prompt/prompt_template.yaml")
|
21
25
|
prompt.input_variables # ["adjective", "content"]
|
@@ -4,7 +4,7 @@ require "langchain"
|
|
4
4
|
# or add `gem "chroma-db", "~> 0.3.0"` to your Gemfile
|
5
5
|
|
6
6
|
# Instantiate the Chroma client
|
7
|
-
chroma = Vectorsearch::Chroma.new(
|
7
|
+
chroma = Langchain::Vectorsearch::Chroma.new(
|
8
8
|
url: ENV["CHROMA_URL"],
|
9
9
|
index_name: "documents",
|
10
10
|
llm: Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
|
@@ -4,7 +4,7 @@ require "langchain"
|
|
4
4
|
# or add `gem "pinecone"` to your Gemfile
|
5
5
|
|
6
6
|
# Instantiate the Qdrant client
|
7
|
-
pinecone = Vectorsearch::Pinecone.new(
|
7
|
+
pinecone = Langchain::Vectorsearch::Pinecone.new(
|
8
8
|
environment: ENV["PINECONE_ENVIRONMENT"],
|
9
9
|
api_key: ENV["PINECONE_API_KEY"],
|
10
10
|
index_name: "recipes",
|
@@ -37,7 +37,7 @@ pinecone.ask(
|
|
37
37
|
)
|
38
38
|
|
39
39
|
# Generate your an embedding and search by it
|
40
|
-
openai = LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
|
40
|
+
openai = Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
|
41
41
|
embedding = openai.embed(text: "veggie")
|
42
42
|
|
43
43
|
pinecone.similarity_search_by_vector(
|
@@ -4,7 +4,7 @@ require "langchain"
|
|
4
4
|
# or add `gem "qdrant-ruby"` to your Gemfile
|
5
5
|
|
6
6
|
# Instantiate the Qdrant client
|
7
|
-
qdrant = Vectorsearch::Qdrant.new(
|
7
|
+
qdrant = Langchain::Vectorsearch::Qdrant.new(
|
8
8
|
url: ENV["QDRANT_URL"],
|
9
9
|
api_key: ENV["QDRANT_API_KEY"],
|
10
10
|
index_name: "recipes",
|
@@ -4,7 +4,7 @@ require "langchain"
|
|
4
4
|
# or add `gem "weaviate-ruby"` to your Gemfile
|
5
5
|
|
6
6
|
# Instantiate the Weaviate client
|
7
|
-
weaviate = Vectorsearch::Weaviate.new(
|
7
|
+
weaviate = Langchain::Vectorsearch::Weaviate.new(
|
8
8
|
url: ENV["WEAVIATE_URL"],
|
9
9
|
api_key: ENV["WEAVIATE_API_KEY"],
|
10
10
|
index_name: "Recipes",
|
@@ -39,11 +39,8 @@ module Langchain::Agent
|
|
39
39
|
|
40
40
|
loop do
|
41
41
|
Langchain.logger.info("[#{self.class.name}]".red + ": Sending the prompt to the #{llm.class} LLM")
|
42
|
-
|
43
|
-
|
44
|
-
stop_sequences: ["Observation:"],
|
45
|
-
max_tokens: 500
|
46
|
-
)
|
42
|
+
|
43
|
+
response = llm.complete(prompt: prompt, stop_sequences: ["Observation:"])
|
47
44
|
|
48
45
|
# Append the response to the prompt
|
49
46
|
prompt += response
|
@@ -22,12 +22,12 @@ module Langchain::Agent
|
|
22
22
|
# @param question [String] Question to ask the LLM/Database
|
23
23
|
# @return [String] Answer to the question
|
24
24
|
#
|
25
|
-
def
|
25
|
+
def run(question:)
|
26
26
|
prompt = create_prompt_for_sql(question: question)
|
27
27
|
|
28
28
|
# Get the SQL string to execute
|
29
29
|
Langchain.logger.info("[#{self.class.name}]".red + ": Passing the inital prompt to the #{llm.class} LLM")
|
30
|
-
sql_string = llm.complete(prompt: prompt
|
30
|
+
sql_string = llm.complete(prompt: prompt)
|
31
31
|
|
32
32
|
# Execute the SQL string and collect the results
|
33
33
|
Langchain.logger.info("[#{self.class.name}]".red + ": Passing the SQL to the Database: #{sql_string}")
|
@@ -36,7 +36,7 @@ module Langchain::Agent
|
|
36
36
|
# Pass the results and get the LLM to synthesize the answer to the question
|
37
37
|
Langchain.logger.info("[#{self.class.name}]".red + ": Passing the synthesize prompt to the #{llm.class} LLM with results: #{results}")
|
38
38
|
prompt2 = create_prompt_for_answer(question: question, sql_query: sql_string, results: results)
|
39
|
-
llm.complete(prompt: prompt2
|
39
|
+
llm.complete(prompt: prompt2)
|
40
40
|
end
|
41
41
|
|
42
42
|
private
|
data/lib/langchain/llm/openai.rb
CHANGED
@@ -35,7 +35,7 @@ module Langchain::LLM
|
|
35
35
|
def embed(text:, **params)
|
36
36
|
parameters = {model: DEFAULTS[:embeddings_model_name], input: text}
|
37
37
|
|
38
|
-
Langchain::Utils::TokenLengthValidator.
|
38
|
+
Langchain::Utils::TokenLengthValidator.validate_max_tokens!(text, parameters[:model])
|
39
39
|
|
40
40
|
response = client.embeddings(parameters: parameters.merge(params))
|
41
41
|
response.dig("data").first.dig("embedding")
|
@@ -50,9 +50,8 @@ module Langchain::LLM
|
|
50
50
|
def complete(prompt:, **params)
|
51
51
|
parameters = compose_parameters DEFAULTS[:completion_model_name], params
|
52
52
|
|
53
|
-
Langchain::Utils::TokenLengthValidator.validate!(prompt, parameters[:model])
|
54
|
-
|
55
53
|
parameters[:prompt] = prompt
|
54
|
+
parameters[:max_tokens] = Langchain::Utils::TokenLengthValidator.validate_max_tokens!(prompt, parameters[:model])
|
56
55
|
|
57
56
|
response = client.completions(parameters: parameters)
|
58
57
|
response.dig("choices", 0, "text")
|
@@ -67,9 +66,8 @@ module Langchain::LLM
|
|
67
66
|
def chat(prompt:, **params)
|
68
67
|
parameters = compose_parameters DEFAULTS[:chat_completion_model_name], params
|
69
68
|
|
70
|
-
Langchain::Utils::TokenLengthValidator.validate!(prompt, parameters[:model])
|
71
|
-
|
72
69
|
parameters[:messages] = [{role: "user", content: prompt}]
|
70
|
+
parameters[:max_tokens] = Langchain::Utils::TokenLengthValidator.validate_max_tokens!(prompt, parameters[:model])
|
73
71
|
|
74
72
|
response = client.chat(parameters: parameters)
|
75
73
|
response.dig("choices", 0, "message", "content")
|
@@ -87,12 +85,7 @@ module Langchain::LLM
|
|
87
85
|
)
|
88
86
|
prompt = prompt_template.format(text: text)
|
89
87
|
|
90
|
-
complete(
|
91
|
-
prompt: prompt,
|
92
|
-
temperature: DEFAULTS[:temperature],
|
93
|
-
# Most models have a context length of 2048 tokens (except for the newest models, which support 4096).
|
94
|
-
max_tokens: 2048
|
95
|
-
)
|
88
|
+
complete(prompt: prompt, temperature: DEFAULTS[:temperature])
|
96
89
|
end
|
97
90
|
|
98
91
|
private
|
@@ -38,7 +38,8 @@ module Langchain::Tool
|
|
38
38
|
hash_results = Langchain::Tool::SerpApi
|
39
39
|
.new(api_key: ENV["SERPAPI_API_KEY"])
|
40
40
|
.execute_search(input: input)
|
41
|
-
hash_results.dig(:answer_box, :to)
|
41
|
+
hash_results.dig(:answer_box, :to) ||
|
42
|
+
hash_results.dig(:answer_box, :result)
|
42
43
|
end
|
43
44
|
end
|
44
45
|
end
|
@@ -34,23 +34,50 @@ module Langchain
|
|
34
34
|
"ada" => 2049
|
35
35
|
}.freeze
|
36
36
|
|
37
|
+
# GOOGLE_PALM_TOKEN_LIMITS = {
|
38
|
+
# "chat-bison-001" => {
|
39
|
+
# "inputTokenLimit"=>4096,
|
40
|
+
# "outputTokenLimit"=>1024
|
41
|
+
# },
|
42
|
+
# "text-bison-001" => {
|
43
|
+
# "inputTokenLimit"=>8196,
|
44
|
+
# "outputTokenLimit"=>1024
|
45
|
+
# },
|
46
|
+
# "embedding-gecko-001" => {
|
47
|
+
# "inputTokenLimit"=>1024
|
48
|
+
# }
|
49
|
+
# }.freeze
|
50
|
+
|
37
51
|
#
|
38
|
-
#
|
52
|
+
# Calculate the `max_tokens:` parameter to be set by calculating the context length of the text minus the prompt length
|
39
53
|
#
|
40
54
|
# @param text [String] The text to validate
|
41
55
|
# @param model_name [String] The model name to validate against
|
42
|
-
# @return [
|
56
|
+
# @return [Integer] Whether the text is valid or not
|
43
57
|
# @raise [TokenLimitExceeded] If the text is too long
|
44
58
|
#
|
45
|
-
def self.
|
46
|
-
|
47
|
-
|
59
|
+
def self.validate_max_tokens!(text, model_name)
|
60
|
+
text_token_length = token_length(text, model_name)
|
61
|
+
max_tokens = TOKEN_LIMITS[model_name] - text_token_length
|
48
62
|
|
49
|
-
if
|
50
|
-
|
63
|
+
# Raise an error even if whole prompt is equal to the model's token limit (max_tokens == 0) since not response will be returned
|
64
|
+
if max_tokens <= 0
|
65
|
+
raise TokenLimitExceeded, "This model's maximum context length is #{TOKEN_LIMITS[model_name]} tokens, but the given text is #{text_token_length} tokens long."
|
51
66
|
end
|
52
67
|
|
53
|
-
|
68
|
+
max_tokens
|
69
|
+
end
|
70
|
+
|
71
|
+
#
|
72
|
+
# Calculate token length for a given text and model name
|
73
|
+
#
|
74
|
+
# @param text [String] The text to validate
|
75
|
+
# @param model_name [String] The model name to validate against
|
76
|
+
# @return [Integer] The token length of the text
|
77
|
+
#
|
78
|
+
def self.token_length(text, model_name)
|
79
|
+
encoder = Tiktoken.encoding_for_model(model_name)
|
80
|
+
encoder.encode(text).length
|
54
81
|
end
|
55
82
|
end
|
56
83
|
end
|
data/lib/langchain/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: langchainrb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.2
|
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-06-
|
11
|
+
date: 2023-06-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: tiktoken_ruby
|