langchainrb 0.9.1 → 0.9.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 +9 -0
- data/README.md +1 -1
- data/lib/langchain/assistants/assistant.rb +5 -3
- data/lib/langchain/output_parsers/output_fixing_parser.rb +6 -5
- data/lib/langchain/processors/eml.rb +65 -0
- data/lib/langchain/tool/base.rb +17 -32
- data/lib/langchain/tool/calculator/calculator.json +19 -0
- data/lib/langchain/tool/{calculator.rb → calculator/calculator.rb} +8 -5
- data/lib/langchain/tool/database/database.json +46 -0
- data/lib/langchain/tool/database/database.rb +105 -0
- data/lib/langchain/tool/google_search/google_search.json +19 -0
- data/lib/langchain/tool/{google_search.rb → google_search/google_search.rb} +5 -6
- data/lib/langchain/tool/ruby_code_interpreter/ruby_code_interpreter.json +19 -0
- data/lib/langchain/tool/{ruby_code_interpreter.rb → ruby_code_interpreter/ruby_code_interpreter.rb} +9 -1
- data/lib/langchain/tool/weather/weather.json +19 -0
- data/lib/langchain/tool/{weather.rb → weather/weather.rb} +3 -4
- data/lib/langchain/tool/wikipedia/wikipedia.json +19 -0
- data/lib/langchain/tool/{wikipedia.rb → wikipedia/wikipedia.rb} +10 -1
- data/lib/langchain/vectorsearch/chroma.rb +3 -1
- data/lib/langchain/vectorsearch/elasticsearch.rb +3 -1
- data/lib/langchain/vectorsearch/epsilla.rb +3 -1
- data/lib/langchain/vectorsearch/milvus.rb +3 -1
- data/lib/langchain/vectorsearch/pgvector.rb +3 -1
- data/lib/langchain/vectorsearch/pinecone.rb +3 -1
- data/lib/langchain/vectorsearch/qdrant.rb +3 -1
- data/lib/langchain/vectorsearch/weaviate.rb +3 -1
- data/lib/langchain/version.rb +1 -1
- data/lib/langchain.rb +7 -0
- metadata +37 -16
- data/lib/langchain/tool/database.rb +0 -90
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b31725a5fdb7c09d25e97b3b8ecbd78eba1eeece6ef2db82f009aa121e1f4956
|
4
|
+
data.tar.gz: 1f9116daf6780682d8c32021212bba702a053af9b4293e488ca605cc298c8e12
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6f50889ce152ac93567951c2a854c5589f5306c8a54c852febc9d5884b3d924904beabfd076e87fefb95354dda99fbb77d179045274ff25bf9515ecee3b2d6bb
|
7
|
+
data.tar.gz: aee9ed10fe48eeef9dc5ba1433145823d20c55042bea7bc359ce5cad5dd4783eb68f1e84bdad79349efea17d77455b9dd6ba918a6a6d0d991e7c350887feac6d
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.9.3]
|
4
|
+
- Add EML processor
|
5
|
+
- Tools can support multiple-methods
|
6
|
+
- Bump gems and bug fixes
|
7
|
+
|
8
|
+
## [0.9.2]
|
9
|
+
- Fix vectorsearch#ask methods
|
10
|
+
- Bump cohere-ruby gem
|
11
|
+
|
3
12
|
## [0.9.1]
|
4
13
|
- Add support for new OpenAI models
|
5
14
|
- Add Ollama#chat method
|
data/README.md
CHANGED
@@ -373,7 +373,7 @@ my_docx = Langchain.root.join("path/to/my.docx")
|
|
373
373
|
|
374
374
|
client.add_data(paths: [my_pdf, my_text, my_docx])
|
375
375
|
```
|
376
|
-
Supported file formats: docx, html, pdf, text, json, jsonl, csv, xlsx.
|
376
|
+
Supported file formats: docx, html, pdf, text, json, jsonl, csv, xlsx, eml.
|
377
377
|
|
378
378
|
Retrieve similar documents based on the query string passed in:
|
379
379
|
```ruby
|
@@ -127,7 +127,7 @@ module Langchain
|
|
127
127
|
params = {messages: thread.openai_messages}
|
128
128
|
|
129
129
|
if tools.any?
|
130
|
-
params[:tools] = tools.map(&:
|
130
|
+
params[:tools] = tools.map(&:to_openai_tools).flatten
|
131
131
|
# TODO: Not sure that tool_choice should always be "auto"; Maybe we can let the user toggle it.
|
132
132
|
params[:tool_choice] = "auto"
|
133
133
|
end
|
@@ -142,14 +142,16 @@ module Langchain
|
|
142
142
|
# Iterate over each function invocation and submit tool output
|
143
143
|
tool_calls.each do |tool_call|
|
144
144
|
tool_call_id = tool_call.dig("id")
|
145
|
-
|
145
|
+
|
146
|
+
function_name = tool_call.dig("function", "name")
|
147
|
+
tool_name, method_name = function_name.split("-")
|
146
148
|
tool_arguments = JSON.parse(tool_call.dig("function", "arguments"), symbolize_names: true)
|
147
149
|
|
148
150
|
tool_instance = tools.find do |t|
|
149
151
|
t.name == tool_name
|
150
152
|
end or raise ArgumentError, "Tool not found in assistant.tools"
|
151
153
|
|
152
|
-
output = tool_instance.
|
154
|
+
output = tool_instance.send(method_name, **tool_arguments)
|
153
155
|
|
154
156
|
submit_tool_output(tool_call_id: tool_call_id, output: output)
|
155
157
|
end
|
@@ -53,11 +53,12 @@ module Langchain::OutputParsers
|
|
53
53
|
parser.parse(completion)
|
54
54
|
rescue OutputParserException => e
|
55
55
|
new_completion = llm.chat(
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
56
|
+
messages: [{role: "user",
|
57
|
+
content: prompt.format(
|
58
|
+
instructions: parser.get_format_instructions,
|
59
|
+
completion: completion,
|
60
|
+
error: e
|
61
|
+
)}]
|
61
62
|
).completion
|
62
63
|
parser.parse(new_completion)
|
63
64
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require "mail"
|
2
|
+
require "uri"
|
3
|
+
|
4
|
+
module Langchain
|
5
|
+
module Processors
|
6
|
+
class Eml < Base
|
7
|
+
EXTENSIONS = [".eml"]
|
8
|
+
CONTENT_TYPES = ["message/rfc822"]
|
9
|
+
|
10
|
+
def initialize(*)
|
11
|
+
depends_on "mail"
|
12
|
+
end
|
13
|
+
|
14
|
+
# Parse the document and return the cleaned text
|
15
|
+
# @param [File] data
|
16
|
+
# @return [String]
|
17
|
+
def parse(data)
|
18
|
+
mail = Mail.read(data.path)
|
19
|
+
text_content = extract_text_content(mail)
|
20
|
+
clean_content(text_content)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# Extract text content from the email, preferring plaintext over HTML
|
26
|
+
def extract_text_content(mail)
|
27
|
+
text_content = ""
|
28
|
+
text_content += "From: #{mail.from}\n" \
|
29
|
+
"To: #{mail.to}\n" \
|
30
|
+
"Cc: #{mail.cc}\n" \
|
31
|
+
"Bcc: #{mail.bcc}\n" \
|
32
|
+
"Subject: #{mail.subject}\n" \
|
33
|
+
"Date: #{mail.date}\n\n"
|
34
|
+
if mail.multipart?
|
35
|
+
mail.parts.each do |part|
|
36
|
+
if part.content_type.start_with?("text/plain")
|
37
|
+
text_content += part.body.decoded.force_encoding("UTF-8").strip + "\n"
|
38
|
+
elsif part.content_type.start_with?("multipart/alternative", "multipart/mixed")
|
39
|
+
text_content += extract_text_content(part) + "\n" # Recursively extract from multipart
|
40
|
+
elsif part.content_type.start_with?("message/rfc822")
|
41
|
+
# Handle embedded .eml parts as separate emails
|
42
|
+
embedded_mail = Mail.read_from_string(part.body.decoded)
|
43
|
+
text_content += "--- Begin Embedded Email ---\n"
|
44
|
+
text_content += extract_text_content(embedded_mail) + "\n"
|
45
|
+
text_content += "--- End Embedded Email ---\n"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
elsif mail.content_type.start_with?("text/plain")
|
49
|
+
text_content = mail.body.decoded.force_encoding("UTF-8").strip
|
50
|
+
end
|
51
|
+
text_content
|
52
|
+
end
|
53
|
+
|
54
|
+
# Clean and format the extracted content
|
55
|
+
def clean_content(content)
|
56
|
+
content
|
57
|
+
.gsub(/\[cid:[^\]]+\]/, "") # Remove embedded image references
|
58
|
+
.gsub(URI::DEFAULT_PARSER.make_regexp(%w[http https])) { |match| "<#{match}>" } # Format URLs
|
59
|
+
.gsub(/\r\n?/, "\n") # Normalize line endings to Unix style
|
60
|
+
.gsub(/[\u200B-\u200D\uFEFF]/, "") # Remove zero width spaces and similar characters
|
61
|
+
.gsub(/<\/?[^>]+>/, "") # Remove any HTML tags that might have sneaked in
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/langchain/tool/base.rb
CHANGED
@@ -48,11 +48,9 @@ module Langchain::Tool
|
|
48
48
|
class Base
|
49
49
|
include Langchain::DependencyHelper
|
50
50
|
|
51
|
-
#
|
52
51
|
# Returns the NAME constant of the tool
|
53
52
|
#
|
54
53
|
# @return [String] tool name
|
55
|
-
#
|
56
54
|
def name
|
57
55
|
self.class.const_get(:NAME)
|
58
56
|
end
|
@@ -63,59 +61,37 @@ module Langchain::Tool
|
|
63
61
|
}
|
64
62
|
end
|
65
63
|
|
66
|
-
#
|
67
64
|
# Returns the DESCRIPTION constant of the tool
|
68
65
|
#
|
69
66
|
# @return [String] tool description
|
70
|
-
#
|
71
67
|
def description
|
72
68
|
self.class.const_get(:DESCRIPTION)
|
73
69
|
end
|
74
70
|
|
75
|
-
#
|
76
71
|
# Sets the DESCRIPTION constant of the tool
|
77
72
|
#
|
78
73
|
# @param value [String] tool description
|
79
|
-
#
|
80
74
|
def self.description(value)
|
81
75
|
const_set(:DESCRIPTION, value.tr("\n", " ").strip)
|
82
76
|
end
|
83
77
|
|
84
|
-
#
|
85
78
|
# Instantiates and executes the tool and returns the answer
|
86
79
|
#
|
87
80
|
# @param input [String] input to the tool
|
88
81
|
# @return [String] answer
|
89
|
-
#
|
90
82
|
def self.execute(input:)
|
83
|
+
warn "DEPRECATED: `#{self}.execute` is deprecated, and will be removed in the next major version."
|
84
|
+
|
91
85
|
new.execute(input: input)
|
92
86
|
end
|
93
87
|
|
94
|
-
# Returns the tool as
|
88
|
+
# Returns the tool as a list of OpenAI formatted functions
|
95
89
|
#
|
96
90
|
# @return [Hash] tool as an OpenAI tool
|
97
|
-
def
|
98
|
-
|
99
|
-
{
|
100
|
-
type: "function",
|
101
|
-
function: {
|
102
|
-
name: name,
|
103
|
-
description: description,
|
104
|
-
parameters: {
|
105
|
-
type: "object",
|
106
|
-
properties: {
|
107
|
-
input: {
|
108
|
-
type: "string",
|
109
|
-
description: "Input to the tool"
|
110
|
-
}
|
111
|
-
},
|
112
|
-
required: ["input"]
|
113
|
-
}
|
114
|
-
}
|
115
|
-
}
|
91
|
+
def to_openai_tools
|
92
|
+
method_annotations
|
116
93
|
end
|
117
94
|
|
118
|
-
#
|
119
95
|
# Executes the tool and returns the answer
|
120
96
|
#
|
121
97
|
# @param input [String] input to the tool
|
@@ -125,12 +101,21 @@ module Langchain::Tool
|
|
125
101
|
raise NotImplementedError, "Your tool must implement the `#execute(input:)` method that returns a string"
|
126
102
|
end
|
127
103
|
|
128
|
-
#
|
104
|
+
# Return tool's method annotations as JSON
|
105
|
+
#
|
106
|
+
# @return [Hash] Tool's method annotations
|
107
|
+
def method_annotations
|
108
|
+
JSON.parse(
|
109
|
+
File.read(
|
110
|
+
self.class.const_get(:ANNOTATIONS_PATH)
|
111
|
+
)
|
112
|
+
)
|
113
|
+
end
|
114
|
+
|
129
115
|
# Validates the list of tools or raises an error
|
130
|
-
# @param tools [Array<Langchain::Tool>] list of tools to be used
|
131
116
|
#
|
117
|
+
# @param tools [Array<Langchain::Tool>] list of tools to be used
|
132
118
|
# @raise [ArgumentError] If any of the tools are not supported
|
133
|
-
#
|
134
119
|
def self.validate_tools!(tools:)
|
135
120
|
# Check if the tool count is equal to unique tool count
|
136
121
|
if tools.count != tools.map(&:name).uniq.count
|
@@ -0,0 +1,19 @@
|
|
1
|
+
[
|
2
|
+
{
|
3
|
+
"type": "function",
|
4
|
+
"function": {
|
5
|
+
"name": "calculator-execute",
|
6
|
+
"description": "Evaluates a pure math expression or if equation contains non-math characters (e.g.: \"12F in Celsius\") then it uses the google search calculator to evaluate the expression",
|
7
|
+
"parameters": {
|
8
|
+
"type": "object",
|
9
|
+
"properties": {
|
10
|
+
"input": {
|
11
|
+
"type": "string",
|
12
|
+
"description": "math expression"
|
13
|
+
}
|
14
|
+
},
|
15
|
+
"required": ["input"]
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
19
|
+
]
|
@@ -6,11 +6,14 @@ module Langchain::Tool
|
|
6
6
|
# A calculator tool that falls back to the Google calculator widget
|
7
7
|
#
|
8
8
|
# Gem requirements:
|
9
|
-
#
|
10
|
-
#
|
9
|
+
# gem "eqn", "~> 1.6.5"
|
10
|
+
# gem "google_search_results", "~> 2.0.0"
|
11
|
+
#
|
12
|
+
# Usage:
|
13
|
+
# calculator = Langchain::Tool::Calculator.new
|
11
14
|
#
|
12
|
-
|
13
15
|
NAME = "calculator"
|
16
|
+
ANNOTATIONS_PATH = Langchain.root.join("./langchain/tool/#{NAME}/#{NAME}.json").to_path
|
14
17
|
|
15
18
|
description <<~DESC
|
16
19
|
Useful for getting the result of a math expression.
|
@@ -27,8 +30,8 @@ module Langchain::Tool
|
|
27
30
|
depends_on "eqn"
|
28
31
|
end
|
29
32
|
|
30
|
-
# Evaluates a pure math expression or if equation contains non-math characters (e.g.: "12F in Celsius") then
|
31
|
-
#
|
33
|
+
# Evaluates a pure math expression or if equation contains non-math characters (e.g.: "12F in Celsius") then it uses the google search calculator to evaluate the expression
|
34
|
+
#
|
32
35
|
# @param input [String] math expression
|
33
36
|
# @return [String] Answer
|
34
37
|
def execute(input:)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
[
|
2
|
+
{
|
3
|
+
"type": "function",
|
4
|
+
"function": {
|
5
|
+
"name": "database-describe_tables",
|
6
|
+
"description": "Database Tool: Returns the schema for a list of tables",
|
7
|
+
"parameters": {
|
8
|
+
"type": "object",
|
9
|
+
"properties": {
|
10
|
+
"tables": {
|
11
|
+
"type": "string",
|
12
|
+
"description": "The tables to describe."
|
13
|
+
}
|
14
|
+
},
|
15
|
+
"required": ["tables"]
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}, {
|
19
|
+
"type": "function",
|
20
|
+
"function": {
|
21
|
+
"name": "database-list_tables",
|
22
|
+
"description": "Database Tool: Returns a list of tables in the database",
|
23
|
+
"parameters": {
|
24
|
+
"type": "object",
|
25
|
+
"properties": {},
|
26
|
+
"required": []
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}, {
|
30
|
+
"type": "function",
|
31
|
+
"function": {
|
32
|
+
"name": "database-execute",
|
33
|
+
"description": "Database Tool: Executes a SQL query and returns the results",
|
34
|
+
"parameters": {
|
35
|
+
"type": "object",
|
36
|
+
"properties": {
|
37
|
+
"input": {
|
38
|
+
"type": "string",
|
39
|
+
"description": "SQL query to be executed"
|
40
|
+
}
|
41
|
+
},
|
42
|
+
"required": ["input"]
|
43
|
+
}
|
44
|
+
}
|
45
|
+
}
|
46
|
+
]
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Langchain::Tool
|
2
|
+
class Database < Base
|
3
|
+
#
|
4
|
+
# Connects to a database, executes SQL queries, and outputs DB schema for Agents to use
|
5
|
+
#
|
6
|
+
# Gem requirements:
|
7
|
+
# gem "sequel", "~> 5.68.0"
|
8
|
+
#
|
9
|
+
# Usage:
|
10
|
+
# database = Langchain::Tool::Database.new(connection_string: "postgres://user:password@localhost:5432/db_name")
|
11
|
+
#
|
12
|
+
NAME = "database"
|
13
|
+
ANNOTATIONS_PATH = Langchain.root.join("./langchain/tool/#{NAME}/#{NAME}.json").to_path
|
14
|
+
|
15
|
+
description <<~DESC
|
16
|
+
Useful for getting the result of a database query.
|
17
|
+
|
18
|
+
The input to this tool should be valid SQL.
|
19
|
+
DESC
|
20
|
+
|
21
|
+
attr_reader :db, :requested_tables, :excluded_tables
|
22
|
+
|
23
|
+
# Establish a database connection
|
24
|
+
#
|
25
|
+
# @param connection_string [String] Database connection info, e.g. 'postgres://user:password@localhost:5432/db_name'
|
26
|
+
# @param tables [Array<Symbol>] The tables to use. Will use all if empty.
|
27
|
+
# @param except_tables [Array<Symbol>] The tables to exclude. Will exclude none if empty.
|
28
|
+
# @return [Database] Database object
|
29
|
+
def initialize(connection_string:, tables: [], exclude_tables: [])
|
30
|
+
depends_on "sequel"
|
31
|
+
|
32
|
+
raise StandardError, "connection_string parameter cannot be blank" if connection_string.empty?
|
33
|
+
|
34
|
+
@db = Sequel.connect(connection_string)
|
35
|
+
@requested_tables = tables
|
36
|
+
@excluded_tables = exclude_tables
|
37
|
+
end
|
38
|
+
|
39
|
+
# Database Tool: Returns a list of tables in the database
|
40
|
+
def list_tables
|
41
|
+
db.tables
|
42
|
+
end
|
43
|
+
|
44
|
+
# Database Tool: Returns the schema for a list of tables
|
45
|
+
#
|
46
|
+
# @param tables [String] The tables to describe.
|
47
|
+
# @return [String] Database schema for the tables
|
48
|
+
def describe_tables(tables:)
|
49
|
+
schema = ""
|
50
|
+
tables.split(",").each do |table|
|
51
|
+
describe_table(table, schema)
|
52
|
+
end
|
53
|
+
schema
|
54
|
+
end
|
55
|
+
|
56
|
+
# Database Tool: Returns the database schema
|
57
|
+
#
|
58
|
+
# @return [String] Database schema
|
59
|
+
def dump_schema
|
60
|
+
Langchain.logger.info("Dumping schema tables and keys", for: self.class)
|
61
|
+
schema = ""
|
62
|
+
db.tables.each do |table|
|
63
|
+
describe_table(table, schema)
|
64
|
+
end
|
65
|
+
schema
|
66
|
+
end
|
67
|
+
|
68
|
+
def describe_table(table, schema)
|
69
|
+
primary_key_columns = []
|
70
|
+
primary_key_column_count = db.schema(table).count { |column| column[1][:primary_key] == true }
|
71
|
+
|
72
|
+
schema << "CREATE TABLE #{table}(\n"
|
73
|
+
db.schema(table).each do |column|
|
74
|
+
schema << "#{column[0]} #{column[1][:type]}"
|
75
|
+
if column[1][:primary_key] == true
|
76
|
+
schema << " PRIMARY KEY" if primary_key_column_count == 1
|
77
|
+
else
|
78
|
+
primary_key_columns << column[0]
|
79
|
+
end
|
80
|
+
schema << ",\n" unless column == db.schema(table).last && primary_key_column_count == 1
|
81
|
+
end
|
82
|
+
if primary_key_column_count > 1
|
83
|
+
schema << "PRIMARY KEY (#{primary_key_columns.join(",")})"
|
84
|
+
end
|
85
|
+
db.foreign_key_list(table).each do |fk|
|
86
|
+
schema << ",\n" if fk == db.foreign_key_list(table).first
|
87
|
+
schema << "FOREIGN KEY (#{fk[:columns][0]}) REFERENCES #{fk[:table]}(#{fk[:key][0]})"
|
88
|
+
schema << ",\n" unless fk == db.foreign_key_list(table).last
|
89
|
+
end
|
90
|
+
schema << ");\n"
|
91
|
+
end
|
92
|
+
|
93
|
+
# Database Tool: Executes a SQL query and returns the results
|
94
|
+
#
|
95
|
+
# @param input [String] SQL query to be executed
|
96
|
+
# @return [Array] Results from the SQL query
|
97
|
+
def execute(input:)
|
98
|
+
Langchain.logger.info("Executing \"#{input}\"", for: self.class)
|
99
|
+
|
100
|
+
db[input].to_a
|
101
|
+
rescue Sequel::DatabaseError => e
|
102
|
+
Langchain.logger.error(e.message, for: self.class)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
[
|
2
|
+
{
|
3
|
+
"type": "function",
|
4
|
+
"function": {
|
5
|
+
"name": "google_search-execute",
|
6
|
+
"description": "Executes Google Search and returns the result",
|
7
|
+
"parameters": {
|
8
|
+
"type": "object",
|
9
|
+
"properties": {
|
10
|
+
"input": {
|
11
|
+
"type": "string",
|
12
|
+
"description": "search query"
|
13
|
+
}
|
14
|
+
},
|
15
|
+
"required": ["input"]
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
19
|
+
]
|
@@ -5,14 +5,15 @@ module Langchain::Tool
|
|
5
5
|
#
|
6
6
|
# Wrapper around SerpApi's Google Search API
|
7
7
|
#
|
8
|
-
# Gem requirements:
|
8
|
+
# Gem requirements:
|
9
|
+
# gem "google_search_results", "~> 2.0.0"
|
9
10
|
#
|
10
11
|
# Usage:
|
11
|
-
#
|
12
|
-
#
|
12
|
+
# search = Langchain::Tool::GoogleSearch.new(api_key: "YOUR_API_KEY")
|
13
|
+
# search.execute(input: "What is the capital of France?")
|
13
14
|
#
|
14
|
-
|
15
15
|
NAME = "google_search"
|
16
|
+
ANNOTATIONS_PATH = Langchain.root.join("./langchain/tool/#{NAME}/#{NAME}.json").to_path
|
16
17
|
|
17
18
|
description <<~DESC
|
18
19
|
A wrapper around SerpApi's Google Search API.
|
@@ -44,12 +45,10 @@ module Langchain::Tool
|
|
44
45
|
new.execute_search(input: input)
|
45
46
|
end
|
46
47
|
|
47
|
-
#
|
48
48
|
# Executes Google Search and returns the result
|
49
49
|
#
|
50
50
|
# @param input [String] search query
|
51
51
|
# @return [String] Answer
|
52
|
-
#
|
53
52
|
def execute(input:)
|
54
53
|
Langchain.logger.info("Executing \"#{input}\"", for: self.class)
|
55
54
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
[
|
2
|
+
{
|
3
|
+
"type": "function",
|
4
|
+
"function": {
|
5
|
+
"name": "ruby_code_interpreter-execute",
|
6
|
+
"description": "Executes Ruby code in a sandboxes environment.",
|
7
|
+
"parameters": {
|
8
|
+
"type": "object",
|
9
|
+
"properties": {
|
10
|
+
"input": {
|
11
|
+
"type": "string",
|
12
|
+
"description": "ruby code expression"
|
13
|
+
}
|
14
|
+
},
|
15
|
+
"required": ["input"]
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
19
|
+
]
|
data/lib/langchain/tool/{ruby_code_interpreter.rb → ruby_code_interpreter/ruby_code_interpreter.rb}
RENAMED
@@ -5,9 +5,15 @@ module Langchain::Tool
|
|
5
5
|
#
|
6
6
|
# A tool that execute Ruby code in a sandboxed environment.
|
7
7
|
#
|
8
|
-
# Gem requirements:
|
8
|
+
# Gem requirements:
|
9
|
+
# gem "safe_ruby", "~> 1.0.4"
|
10
|
+
#
|
11
|
+
# Usage:
|
12
|
+
# interpreter = Langchain::Tool::RubyCodeInterpreter.new
|
9
13
|
#
|
10
14
|
NAME = "ruby_code_interpreter"
|
15
|
+
ANNOTATIONS_PATH = Langchain.root.join("./langchain/tool/#{NAME}/#{NAME}.json").to_path
|
16
|
+
|
11
17
|
description <<~DESC
|
12
18
|
A Ruby code interpreter. Use this to execute ruby expressions. Input should be a valid ruby expression. If you want to see the output of the tool, make sure to return a value.
|
13
19
|
DESC
|
@@ -18,6 +24,8 @@ module Langchain::Tool
|
|
18
24
|
@timeout = timeout
|
19
25
|
end
|
20
26
|
|
27
|
+
# Executes Ruby code in a sandboxes environment.
|
28
|
+
#
|
21
29
|
# @param input [String] ruby code expression
|
22
30
|
# @return [String] Answer
|
23
31
|
def execute(input:)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
[
|
2
|
+
{
|
3
|
+
"type": "function",
|
4
|
+
"function": {
|
5
|
+
"name": "weather-execute",
|
6
|
+
"description": "Returns current weather for a city",
|
7
|
+
"parameters": {
|
8
|
+
"type": "object",
|
9
|
+
"properties": {
|
10
|
+
"input": {
|
11
|
+
"type": "string",
|
12
|
+
"description": "comma separated city and unit (optional: imperial, metric, or standard)"
|
13
|
+
}
|
14
|
+
},
|
15
|
+
"required": ["input"]
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
19
|
+
]
|
@@ -13,11 +13,11 @@ module Langchain::Tool
|
|
13
13
|
# api_key: https://home.openweathermap.org/api_keys
|
14
14
|
#
|
15
15
|
# Usage:
|
16
|
-
# weather = Langchain::Tool::Weather.new(api_key: "
|
16
|
+
# weather = Langchain::Tool::Weather.new(api_key: ENV["OPEN_WEATHER_API_KEY"])
|
17
17
|
# weather.execute(input: "Boston, MA; imperial")
|
18
18
|
#
|
19
|
-
|
20
19
|
NAME = "weather"
|
20
|
+
ANNOTATIONS_PATH = Langchain.root.join("./langchain/tool/#{NAME}/#{NAME}.json").to_path
|
21
21
|
|
22
22
|
description <<~DESC
|
23
23
|
Useful for getting current weather data
|
@@ -32,12 +32,10 @@ module Langchain::Tool
|
|
32
32
|
|
33
33
|
attr_reader :client, :units
|
34
34
|
|
35
|
-
#
|
36
35
|
# Initializes the Weather tool
|
37
36
|
#
|
38
37
|
# @param api_key [String] Open Weather API key
|
39
38
|
# @return [Langchain::Tool::Weather] Weather tool
|
40
|
-
#
|
41
39
|
def initialize(api_key:, units: "metric")
|
42
40
|
depends_on "open-weather-ruby-client"
|
43
41
|
require "open-weather-ruby-client"
|
@@ -51,6 +49,7 @@ module Langchain::Tool
|
|
51
49
|
end
|
52
50
|
|
53
51
|
# Returns current weather for a city
|
52
|
+
#
|
54
53
|
# @param input [String] comma separated city and unit (optional: imperial, metric, or standard)
|
55
54
|
# @return [String] Answer
|
56
55
|
def execute(input:)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
[
|
2
|
+
{
|
3
|
+
"type": "function",
|
4
|
+
"function": {
|
5
|
+
"name": "wikipedia-execute",
|
6
|
+
"description": "Executes Wikipedia API search and returns the answer",
|
7
|
+
"parameters": {
|
8
|
+
"type": "object",
|
9
|
+
"properties": {
|
10
|
+
"input": {
|
11
|
+
"type": "string",
|
12
|
+
"description": "search query"
|
13
|
+
}
|
14
|
+
},
|
15
|
+
"required": ["input"]
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
19
|
+
]
|
@@ -5,9 +5,16 @@ module Langchain::Tool
|
|
5
5
|
#
|
6
6
|
# Tool that adds the capability to search using the Wikipedia API
|
7
7
|
#
|
8
|
-
# Gem requirements:
|
8
|
+
# Gem requirements:
|
9
|
+
# gem "wikipedia-client", "~> 1.17.0"
|
10
|
+
#
|
11
|
+
# Usage:
|
12
|
+
# weather = Langchain::Tool::Wikipedia.new
|
13
|
+
# weather.execute(input: "The Roman Empire")
|
9
14
|
#
|
10
15
|
NAME = "wikipedia"
|
16
|
+
ANNOTATIONS_PATH = Langchain.root.join("./langchain/tool/#{NAME}/#{NAME}.json").to_path
|
17
|
+
|
11
18
|
description <<~DESC
|
12
19
|
A wrapper around Wikipedia.
|
13
20
|
|
@@ -17,11 +24,13 @@ module Langchain::Tool
|
|
17
24
|
Input should be a search query.
|
18
25
|
DESC
|
19
26
|
|
27
|
+
# Initializes the Wikipedia tool
|
20
28
|
def initialize
|
21
29
|
depends_on "wikipedia-client", req: "wikipedia"
|
22
30
|
end
|
23
31
|
|
24
32
|
# Executes Wikipedia API search and returns the answer
|
33
|
+
#
|
25
34
|
# @param input [String] search query
|
26
35
|
# @return [String] Answer
|
27
36
|
def execute(input:)
|
@@ -126,7 +126,9 @@ module Langchain::Vectorsearch
|
|
126
126
|
|
127
127
|
prompt = generate_rag_prompt(question: question, context: context)
|
128
128
|
|
129
|
-
|
129
|
+
messages = [{role: "user", content: prompt}]
|
130
|
+
response = llm.chat(messages: messages, &block)
|
131
|
+
|
130
132
|
response.context = context
|
131
133
|
response
|
132
134
|
end
|
@@ -141,7 +141,9 @@ module Langchain::Vectorsearch
|
|
141
141
|
|
142
142
|
prompt = generate_rag_prompt(question: question, context: context)
|
143
143
|
|
144
|
-
|
144
|
+
messages = [{role: "user", content: prompt}]
|
145
|
+
response = llm.chat(messages: messages, &block)
|
146
|
+
|
145
147
|
response.context = context
|
146
148
|
response
|
147
149
|
end
|
@@ -139,7 +139,9 @@ module Langchain::Vectorsearch
|
|
139
139
|
|
140
140
|
prompt = generate_rag_prompt(question: question, context: context)
|
141
141
|
|
142
|
-
|
142
|
+
messages = [{role: "user", content: prompt}]
|
143
|
+
response = llm.chat(messages: messages, &block)
|
144
|
+
|
143
145
|
response.context = context
|
144
146
|
response
|
145
147
|
end
|
@@ -151,7 +151,9 @@ module Langchain::Vectorsearch
|
|
151
151
|
|
152
152
|
prompt = generate_rag_prompt(question: question, context: context)
|
153
153
|
|
154
|
-
|
154
|
+
messages = [{role: "user", content: prompt}]
|
155
|
+
response = llm.chat(messages: messages, &block)
|
156
|
+
|
155
157
|
response.context = context
|
156
158
|
response
|
157
159
|
end
|
@@ -148,7 +148,9 @@ module Langchain::Vectorsearch
|
|
148
148
|
|
149
149
|
prompt = generate_rag_prompt(question: question, context: context)
|
150
150
|
|
151
|
-
|
151
|
+
messages = [{role: "user", content: prompt}]
|
152
|
+
response = llm.chat(messages: messages, &block)
|
153
|
+
|
152
154
|
response.context = context
|
153
155
|
response
|
154
156
|
end
|
@@ -181,7 +181,9 @@ module Langchain::Vectorsearch
|
|
181
181
|
|
182
182
|
prompt = generate_rag_prompt(question: question, context: context)
|
183
183
|
|
184
|
-
|
184
|
+
messages = [{role: "user", content: prompt}]
|
185
|
+
response = llm.chat(messages: messages, &block)
|
186
|
+
|
185
187
|
response.context = context
|
186
188
|
response
|
187
189
|
end
|
@@ -137,7 +137,9 @@ module Langchain::Vectorsearch
|
|
137
137
|
|
138
138
|
prompt = generate_rag_prompt(question: question, context: context)
|
139
139
|
|
140
|
-
|
140
|
+
messages = [{role: "user", content: prompt}]
|
141
|
+
response = llm.chat(messages: messages, &block)
|
142
|
+
|
141
143
|
response.context = context
|
142
144
|
response
|
143
145
|
end
|
@@ -137,7 +137,9 @@ module Langchain::Vectorsearch
|
|
137
137
|
|
138
138
|
prompt = generate_rag_prompt(question: question, context: context)
|
139
139
|
|
140
|
-
|
140
|
+
messages = [{role: "user", content: prompt}]
|
141
|
+
response = llm.chat(messages: messages, &block)
|
142
|
+
|
141
143
|
response.context = context
|
142
144
|
response
|
143
145
|
end
|
data/lib/langchain/version.rb
CHANGED
data/lib/langchain.rb
CHANGED
@@ -25,6 +25,13 @@ loader.inflector.inflect(
|
|
25
25
|
)
|
26
26
|
loader.collapse("#{__dir__}/langchain/llm/response")
|
27
27
|
loader.collapse("#{__dir__}/langchain/assistants")
|
28
|
+
|
29
|
+
loader.collapse("#{__dir__}/langchain/tool/calculator")
|
30
|
+
loader.collapse("#{__dir__}/langchain/tool/database")
|
31
|
+
loader.collapse("#{__dir__}/langchain/tool/google_search")
|
32
|
+
loader.collapse("#{__dir__}/langchain/tool/ruby_code_interpreter")
|
33
|
+
loader.collapse("#{__dir__}/langchain/tool/weather")
|
34
|
+
loader.collapse("#{__dir__}/langchain/tool/wikipedia")
|
28
35
|
loader.setup
|
29
36
|
|
30
37
|
# Langchain.rb a is library for building LLM-backed Ruby applications. It is an abstraction layer that sits on top of the emerging AI-related tools that makes it easy for developers to consume and string those services together.
|
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.9.
|
4
|
+
version: 0.9.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: 2024-02-
|
11
|
+
date: 2024-02-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: baran
|
@@ -30,42 +30,42 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 1.1.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 1.1.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: tiktoken_ruby
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 0.0.
|
47
|
+
version: 0.0.7
|
48
48
|
type: :runtime
|
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: 0.0.
|
54
|
+
version: 0.0.7
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: json-schema
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 4
|
61
|
+
version: '4'
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 4
|
68
|
+
version: '4'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: zeitwerk
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -240,14 +240,14 @@ dependencies:
|
|
240
240
|
requirements:
|
241
241
|
- - "~>"
|
242
242
|
- !ruby/object:Gem::Version
|
243
|
-
version: 0.9.
|
243
|
+
version: 0.9.8
|
244
244
|
type: :development
|
245
245
|
prerelease: false
|
246
246
|
version_requirements: !ruby/object:Gem::Requirement
|
247
247
|
requirements:
|
248
248
|
- - "~>"
|
249
249
|
- !ruby/object:Gem::Version
|
250
|
-
version: 0.9.
|
250
|
+
version: 0.9.8
|
251
251
|
- !ruby/object:Gem::Dependency
|
252
252
|
name: docx
|
253
253
|
requirement: !ruby/object:Gem::Requirement
|
@@ -416,6 +416,20 @@ dependencies:
|
|
416
416
|
- - "~>"
|
417
417
|
- !ruby/object:Gem::Version
|
418
418
|
version: '1.13'
|
419
|
+
- !ruby/object:Gem::Dependency
|
420
|
+
name: mail
|
421
|
+
requirement: !ruby/object:Gem::Requirement
|
422
|
+
requirements:
|
423
|
+
- - "~>"
|
424
|
+
- !ruby/object:Gem::Version
|
425
|
+
version: '2.8'
|
426
|
+
type: :development
|
427
|
+
prerelease: false
|
428
|
+
version_requirements: !ruby/object:Gem::Requirement
|
429
|
+
requirements:
|
430
|
+
- - "~>"
|
431
|
+
- !ruby/object:Gem::Version
|
432
|
+
version: '2.8'
|
419
433
|
- !ruby/object:Gem::Dependency
|
420
434
|
name: open-weather-ruby-client
|
421
435
|
requirement: !ruby/object:Gem::Requirement
|
@@ -692,6 +706,7 @@ files:
|
|
692
706
|
- lib/langchain/processors/base.rb
|
693
707
|
- lib/langchain/processors/csv.rb
|
694
708
|
- lib/langchain/processors/docx.rb
|
709
|
+
- lib/langchain/processors/eml.rb
|
695
710
|
- lib/langchain/processors/html.rb
|
696
711
|
- lib/langchain/processors/json.rb
|
697
712
|
- lib/langchain/processors/jsonl.rb
|
@@ -705,12 +720,18 @@ files:
|
|
705
720
|
- lib/langchain/prompt/loading.rb
|
706
721
|
- lib/langchain/prompt/prompt_template.rb
|
707
722
|
- lib/langchain/tool/base.rb
|
708
|
-
- lib/langchain/tool/calculator.
|
709
|
-
- lib/langchain/tool/
|
710
|
-
- lib/langchain/tool/
|
711
|
-
- lib/langchain/tool/
|
712
|
-
- lib/langchain/tool/
|
713
|
-
- lib/langchain/tool/
|
723
|
+
- lib/langchain/tool/calculator/calculator.json
|
724
|
+
- lib/langchain/tool/calculator/calculator.rb
|
725
|
+
- lib/langchain/tool/database/database.json
|
726
|
+
- lib/langchain/tool/database/database.rb
|
727
|
+
- lib/langchain/tool/google_search/google_search.json
|
728
|
+
- lib/langchain/tool/google_search/google_search.rb
|
729
|
+
- lib/langchain/tool/ruby_code_interpreter/ruby_code_interpreter.json
|
730
|
+
- lib/langchain/tool/ruby_code_interpreter/ruby_code_interpreter.rb
|
731
|
+
- lib/langchain/tool/weather/weather.json
|
732
|
+
- lib/langchain/tool/weather/weather.rb
|
733
|
+
- lib/langchain/tool/wikipedia/wikipedia.json
|
734
|
+
- lib/langchain/tool/wikipedia/wikipedia.rb
|
714
735
|
- lib/langchain/utils/cosine_similarity.rb
|
715
736
|
- lib/langchain/utils/token_length/ai21_validator.rb
|
716
737
|
- lib/langchain/utils/token_length/base_validator.rb
|
@@ -1,90 +0,0 @@
|
|
1
|
-
module Langchain::Tool
|
2
|
-
class Database < Base
|
3
|
-
#
|
4
|
-
# Connects to a database, executes SQL queries, and outputs DB schema for Agents to use
|
5
|
-
#
|
6
|
-
# Gem requirements: gem "sequel", "~> 5.68.0"
|
7
|
-
#
|
8
|
-
|
9
|
-
NAME = "database"
|
10
|
-
|
11
|
-
description <<~DESC
|
12
|
-
Useful for getting the result of a database query.
|
13
|
-
|
14
|
-
The input to this tool should be valid SQL.
|
15
|
-
DESC
|
16
|
-
|
17
|
-
attr_reader :db, :requested_tables, :excluded_tables
|
18
|
-
|
19
|
-
#
|
20
|
-
# Establish a database connection
|
21
|
-
#
|
22
|
-
# @param connection_string [String] Database connection info, e.g. 'postgres://user:password@localhost:5432/db_name'
|
23
|
-
# @param tables [Array<Symbol>] The tables to use. Will use all if empty.
|
24
|
-
# @param except_tables [Array<Symbol>] The tables to exclude. Will exclude none if empty.
|
25
|
-
|
26
|
-
# @return [Database] Database object
|
27
|
-
#
|
28
|
-
def initialize(connection_string:, tables: [], exclude_tables: [])
|
29
|
-
depends_on "sequel"
|
30
|
-
|
31
|
-
raise StandardError, "connection_string parameter cannot be blank" if connection_string.empty?
|
32
|
-
|
33
|
-
@db = Sequel.connect(connection_string)
|
34
|
-
@requested_tables = tables
|
35
|
-
@excluded_tables = exclude_tables
|
36
|
-
end
|
37
|
-
|
38
|
-
#
|
39
|
-
# Returns the database schema
|
40
|
-
#
|
41
|
-
# @return [String] schema
|
42
|
-
#
|
43
|
-
def dump_schema
|
44
|
-
Langchain.logger.info("Dumping schema tables and keys", for: self.class)
|
45
|
-
schema = ""
|
46
|
-
db.tables.each do |table|
|
47
|
-
next if excluded_tables.include?(table)
|
48
|
-
next unless requested_tables.empty? || requested_tables.include?(table)
|
49
|
-
|
50
|
-
primary_key_columns = []
|
51
|
-
primary_key_column_count = db.schema(table).count { |column| column[1][:primary_key] == true }
|
52
|
-
|
53
|
-
schema << "CREATE TABLE #{table}(\n"
|
54
|
-
db.schema(table).each do |column|
|
55
|
-
schema << "#{column[0]} #{column[1][:type]}"
|
56
|
-
if column[1][:primary_key] == true
|
57
|
-
schema << " PRIMARY KEY" if primary_key_column_count == 1
|
58
|
-
else
|
59
|
-
primary_key_columns << column[0]
|
60
|
-
end
|
61
|
-
schema << ",\n" unless column == db.schema(table).last && primary_key_column_count == 1
|
62
|
-
end
|
63
|
-
if primary_key_column_count > 1
|
64
|
-
schema << "PRIMARY KEY (#{primary_key_columns.join(",")})"
|
65
|
-
end
|
66
|
-
db.foreign_key_list(table).each do |fk|
|
67
|
-
schema << ",\n" if fk == db.foreign_key_list(table).first
|
68
|
-
schema << "FOREIGN KEY (#{fk[:columns][0]}) REFERENCES #{fk[:table]}(#{fk[:key][0]})"
|
69
|
-
schema << ",\n" unless fk == db.foreign_key_list(table).last
|
70
|
-
end
|
71
|
-
schema << ");\n"
|
72
|
-
end
|
73
|
-
schema
|
74
|
-
end
|
75
|
-
|
76
|
-
#
|
77
|
-
# Evaluates a sql expression
|
78
|
-
#
|
79
|
-
# @param input [String] sql expression
|
80
|
-
# @return [Array] results
|
81
|
-
#
|
82
|
-
def execute(input:)
|
83
|
-
Langchain.logger.info("Executing \"#{input}\"", for: self.class)
|
84
|
-
|
85
|
-
db[input].to_a
|
86
|
-
rescue Sequel::DatabaseError => e
|
87
|
-
Langchain.logger.error(e.message, for: self.class)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|