langchainrb 0.9.2 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cdaafd0889d6666c7aa39d8bee71763f8baf86e0566f9e87312fc6033d07709d
4
- data.tar.gz: 7acc9f122aed92eab9ab2ac1d50f7ee59c0e24030335dc15878769b7664ffc6b
3
+ metadata.gz: b31725a5fdb7c09d25e97b3b8ecbd78eba1eeece6ef2db82f009aa121e1f4956
4
+ data.tar.gz: 1f9116daf6780682d8c32021212bba702a053af9b4293e488ca605cc298c8e12
5
5
  SHA512:
6
- metadata.gz: b76f62411f75eccba98371791e63b18f0f22225de1af7449cde40680acbd1dc09c4b5a6b7eeb1ac667dd9b029c52534fbfbe10a5fa465fa0859059ddb32a400c
7
- data.tar.gz: 64052a22206ece081a2fd9fbf0233ba47f40539743b1def6d7fc9beeb31102f5559878514cbc0d503fd13681e5efbb34c2755cacbd62b1b01b67876bc9d6237e
6
+ metadata.gz: 6f50889ce152ac93567951c2a854c5589f5306c8a54c852febc9d5884b3d924904beabfd076e87fefb95354dda99fbb77d179045274ff25bf9515ecee3b2d6bb
7
+ data.tar.gz: aee9ed10fe48eeef9dc5ba1433145823d20c55042bea7bc359ce5cad5dd4783eb68f1e84bdad79349efea17d77455b9dd6ba918a6a6d0d991e7c350887feac6d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
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
+
3
8
  ## [0.9.2]
4
9
  - Fix vectorsearch#ask methods
5
10
  - Bump cohere-ruby gem
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(&:to_openai_tool)
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
- tool_name = tool_call.dig("function", "name")
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.execute(**tool_arguments)
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
- prompt: prompt.format(
57
- instructions: parser.get_format_instructions,
58
- completion: completion,
59
- error: e
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
@@ -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 an OpenAI tool
88
+ # Returns the tool as a list of OpenAI formatted functions
95
89
  #
96
90
  # @return [Hash] tool as an OpenAI tool
97
- def to_openai_tool
98
- # TODO: This is hardcoded to def execute(input:) found in each tool, needs to be dynamic.
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
- # gem "eqn", "~> 1.6.5"
10
- # gem "google_search_results", "~> 2.0.0"
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
- # it uses the google search calculator to evaluate the expression
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: gem "google_search_results", "~> 2.0.0"
8
+ # Gem requirements:
9
+ # gem "google_search_results", "~> 2.0.0"
9
10
  #
10
11
  # Usage:
11
- # search = Langchain::Tool::GoogleSearch.new(api_key: "YOUR_API_KEY")
12
- # search.execute(input: "What is the capital of France?")
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
+ ]
@@ -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: gem "safe_ruby", "~> 1.0.4"
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: "YOUR_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: gem "wikipedia-client", "~> 1.17.0"
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:)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Langchain
4
- VERSION = "0.9.2"
4
+ VERSION = "0.9.3"
5
5
  end
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.2
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 00:00:00.000000000 Z
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: 0.8.1
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: 0.8.1
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.5
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.5
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.0.0
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.0.0
68
+ version: '4'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: zeitwerk
71
71
  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.rb
709
- - lib/langchain/tool/database.rb
710
- - lib/langchain/tool/google_search.rb
711
- - lib/langchain/tool/ruby_code_interpreter.rb
712
- - lib/langchain/tool/weather.rb
713
- - lib/langchain/tool/wikipedia.rb
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