langchainrb 0.3.2 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
data/lib/tool/base.rb CHANGED
@@ -12,27 +12,39 @@ 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
 
18
- # Executes the tool and returns the answer
19
+ def self.description(value)
20
+ const_set(:DESCRIPTION, value.tr("\n", " ").strip)
21
+ end
22
+
23
+ # Instantiates and executes the tool and returns the answer
19
24
  # @param input [String] input to the tool
20
25
  # @return [String] answer
21
26
  def self.execute(input:)
22
- raise NotImplementedError, "Your tool must implement the `self.execute(input:)` method that returns a string"
27
+ new.execute(input: input)
28
+ end
29
+
30
+ # Executes the tool and returns the answer
31
+ # @param input [String] input to the tool
32
+ # @return [String] answer
33
+ def execute(input:)
34
+ raise NotImplementedError, "Your tool must implement the `#execute(input:)` method that returns a string"
23
35
  end
24
36
 
25
- #
37
+ #
26
38
  # Validates the list of strings (tools) are all supported or raises an error
27
39
  # @param tools [Array<String>] list of tools to be used
28
- #
40
+ #
29
41
  # @raise [ArgumentError] If any of the tools are not supported
30
- #
42
+ #
31
43
  def self.validate_tools!(tools:)
32
- unrecognized_tools = tools - Tool::Base::TOOLS.keys
44
+ unrecognized_tools = tools - Tool::Base::TOOLS.keys
33
45
 
34
46
  if unrecognized_tools.any?
35
- raise ArgumentError, "Unrecognized Tools: #{unrecognized_tools}"
47
+ raise ArgumentError, "Unrecognized Tools: #{unrecognized_tools}"
36
48
  end
37
49
  end
38
50
  end
@@ -1,17 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "eqn"
4
-
5
3
  module Tool
6
4
  class Calculator < Base
7
- DESCRIPTION = "Useful for getting the result of a math expression. " +
8
- "The input to this tool should be a valid mathematical expression that could be executed by a simple calculator."
5
+ description <<~DESC
6
+ Useful for getting the result of a math expression.
7
+
8
+ The input to this tool should be a valid mathematical expression that could be executed by a simple calculator.
9
+ DESC
10
+
11
+ def initialize
12
+ depends_on "eqn"
13
+ require "eqn"
14
+ end
9
15
 
10
16
  # Evaluates a pure math expression or if equation contains non-math characters (e.g.: "12F in Celsius") then
11
17
  # it uses the google search calculator to evaluate the expression
12
18
  # @param input [String] math expression
13
19
  # @return [String] Answer
14
- def self.execute(input:)
20
+ def execute(input:)
15
21
  Eqn::Calculator.calc(input)
16
22
  rescue Eqn::ParseError, Eqn::NoVariableValueError
17
23
  # Sometimes the input is not a pure math expression, e.g: "12F in Celsius"
data/lib/tool/serp_api.rb CHANGED
@@ -1,39 +1,51 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "google_search_results"
4
-
5
3
  module Tool
6
4
  class SerpApi < Base
7
5
  # Wrapper around SerpAPI
8
6
  # Set ENV["SERPAPI_API_KEY"] to use it
9
7
 
10
- DESCRIPTION = "A wrapper around Google Search. " +
11
- "Useful for when you need to answer questions about current events. " +
12
- "Always one of the first options when you need to find information on internet. " +
13
- "Input should be a search query."
8
+ description <<~DESC
9
+ A wrapper around Google Search.
10
+
11
+ Useful for when you need to answer questions about current events.
12
+ Always one of the first options when you need to find information on internet.
13
+
14
+ Input should be a search query.
15
+ DESC
16
+
17
+ def initialize
18
+ depends_on "google_search_results"
19
+ require "google_search_results"
20
+ end
21
+
22
+ # Executes Google Search and returns hash_results JSON
23
+ # @param input [String] search query
24
+ # @return [Hash] hash_results JSON
25
+
26
+ def self.execute_search(input:)
27
+ new.execute_search(input: input)
28
+ end
14
29
 
15
30
  # Executes Google Search and returns hash_results JSON
16
31
  # @param input [String] search query
17
32
  # @return [String] Answer
18
33
  # 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
34
  # We may need to do the same thing here.
20
- def self.execute(input:)
21
- hash_results = self.execute_search(input: input)
35
+ def execute(input:)
36
+ hash_results = execute_search(input: input)
22
37
 
23
38
  hash_results.dig(:answer_box, :answer) ||
24
39
  hash_results.dig(:answer_box, :snippet) ||
25
40
  hash_results.dig(:organic_results, 0, :snippet)
26
41
  end
27
42
 
28
- # Executes Google Search and returns hash_results JSON
29
- # @param input [String] search query
30
- # @return [Hash] hash_results JSON
31
- def self.execute_search(input:)
43
+ def execute_search(input:)
32
44
  GoogleSearch.new(
33
45
  q: input,
34
46
  serp_api_key: ENV["SERPAPI_API_KEY"]
35
47
  )
36
- .get_hash
48
+ .get_hash
37
49
  end
38
50
  end
39
51
  end
@@ -1,23 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'wikipedia'
4
-
5
3
  module Tool
6
4
  class Wikipedia < Base
7
5
  # Tool that adds the capability to search using the Wikipedia API
8
6
 
9
- DESCRIPTION = "A wrapper around Wikipedia. " +
10
- "Useful for when you need to answer general questions about " +
11
- "people, places, companies, facts, historical events, or other subjects. " +
12
- "Input should be a search query."
7
+ description <<~DESC
8
+ A wrapper around Wikipedia.
9
+
10
+ Useful for when you need to answer general questions about
11
+ people, places, companies, facts, historical events, or other subjects.
12
+
13
+ Input should be a search query.
14
+ DESC
15
+
16
+ def initialize
17
+ depends_on "wikipedia-client"
18
+ require "wikipedia"
19
+ end
13
20
 
14
21
  # Executes Wikipedia API search and returns the answer
15
22
  # @param input [String] search query
16
23
  # @return [String] Answer
17
- def self.execute(input:)
24
+ def execute(input:)
18
25
  page = ::Wikipedia.find(input)
19
26
  # It would be nice to figure out a way to provide page.content but the LLM token limit is an issue
20
- page.summary
27
+ page.summary
21
28
  end
22
29
  end
23
30
  end
@@ -1,12 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "forwardable"
4
+
3
5
  module Vectorsearch
4
6
  class Base
5
7
  extend Forwardable
6
8
 
7
9
  attr_reader :client, :index_name, :llm, :llm_api_key, :llm_client
8
10
 
9
- DEFAULT_METRIC = "cosine".freeze
11
+ DEFAULT_METRIC = "cosine"
10
12
 
11
13
  # @param llm [Symbol] The LLM to use
12
14
  # @param llm_api_key [String] The API key for the LLM
@@ -46,7 +48,7 @@ module Vectorsearch
46
48
  input_variables: ["context"]
47
49
  ),
48
50
  examples: [
49
- { context: context }
51
+ {context: context}
50
52
  ],
51
53
  input_variables: ["question"],
52
54
  example_separator: "\n"
@@ -55,4 +57,4 @@ module Vectorsearch
55
57
  prompt_template.format(question: question)
56
58
  end
57
59
  end
58
- end
60
+ end
@@ -1,27 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "milvus"
4
-
5
3
  module Vectorsearch
6
4
  class Milvus < Base
7
- def initialize(
8
- url:,
9
- api_key: nil,
10
- index_name:,
11
- llm:,
12
- llm_api_key:
13
- )
14
- @client = ::Milvus::Client.new(
15
- url: url
16
- )
5
+ def initialize(url:, index_name:, llm:, llm_api_key:, api_key: nil)
6
+ depends_on "milvus"
7
+ require "milvus"
8
+
9
+ @client = ::Milvus::Client.new(url: url)
17
10
  @index_name = index_name
18
11
 
19
12
  super(llm: llm, llm_api_key: llm_api_key)
20
13
  end
21
14
 
22
- def add_texts(
23
- texts:
24
- )
15
+ def add_texts(texts:)
25
16
  client.entities.insert(
26
17
  collection_name: index_name,
27
18
  num_rows: texts.count,
@@ -77,10 +68,7 @@ module Vectorsearch
77
68
  )
78
69
  end
79
70
 
80
- def similarity_search(
81
- query:,
82
- k: 4
83
- )
71
+ def similarity_search(query:, k: 4)
84
72
  embedding = generate_embedding(text: query)
85
73
 
86
74
  similarity_search_by_vector(
@@ -89,14 +77,11 @@ module Vectorsearch
89
77
  )
90
78
  end
91
79
 
92
- def similarity_search_by_vector(
93
- embedding:,
94
- k: 4
95
- )
80
+ def similarity_search_by_vector(embedding:, k: 4)
96
81
  client.search(
97
82
  collection_name: index_name,
98
83
  top_k: k.to_s,
99
- vectors: [ embedding ],
84
+ vectors: [embedding],
100
85
  dsl_type: 1,
101
86
  params: "{\"nprobe\": 10}",
102
87
  anns_field: "content",
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "pinecone"
4
-
5
3
  module Vectorsearch
6
4
  class Pinecone < Base
7
5
  # Initialize the Pinecone client
@@ -10,18 +8,15 @@ module Vectorsearch
10
8
  # @param index_name [String] The name of the index to use
11
9
  # @param llm [Symbol] The LLM to use
12
10
  # @param llm_api_key [String] The API key for the LLM
13
- def initialize(
14
- environment:,
15
- api_key:,
16
- index_name:,
17
- llm:,
18
- llm_api_key:
19
- )
11
+ def initialize(environment:, api_key:, index_name:, llm:, llm_api_key:)
12
+ depends_on "pinecone"
13
+ require "pinecone"
14
+
20
15
  ::Pinecone.configure do |config|
21
- config.api_key = api_key
16
+ config.api_key = api_key
22
17
  config.environment = environment
23
18
  end
24
-
19
+
25
20
  @client = ::Pinecone::Client.new
26
21
  @index_name = index_name
27
22
 
@@ -31,14 +26,12 @@ module Vectorsearch
31
26
  # Add a list of texts to the index
32
27
  # @param texts [Array] The list of texts to add
33
28
  # @return [Hash] The response from the server
34
- def add_texts(
35
- texts:
36
- )
29
+ def add_texts(texts:)
37
30
  vectors = texts.map do |text|
38
31
  {
39
32
  # TODO: Allows passing in your own IDs
40
33
  id: SecureRandom.uuid,
41
- metadata: { content: text },
34
+ metadata: {content: text},
42
35
  values: generate_embedding(text: text)
43
36
  }
44
37
  end
@@ -78,10 +71,7 @@ module Vectorsearch
78
71
  # @param embedding [Array] The embedding to search for
79
72
  # @param k [Integer] The number of results to return
80
73
  # @return [Array] The list of results
81
- def similarity_search_by_vector(
82
- embedding:,
83
- k: 4
84
- )
74
+ def similarity_search_by_vector(embedding:, k: 4)
85
75
  index = client.index(index_name)
86
76
 
87
77
  response = index.query(
@@ -109,4 +99,4 @@ module Vectorsearch
109
99
  generate_completion(prompt: prompt)
110
100
  end
111
101
  end
112
- end
102
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "qdrant"
4
-
5
3
  module Vectorsearch
6
4
  class Qdrant < Base
7
5
  # Initialize the Qdrant client
@@ -10,13 +8,10 @@ module Vectorsearch
10
8
  # @param index_name [String] The name of the index to use
11
9
  # @param llm [Symbol] The LLM to use
12
10
  # @param llm_api_key [String] The API key for the LLM
13
- def initialize(
14
- url:,
15
- api_key:,
16
- index_name:,
17
- llm:,
18
- llm_api_key:
19
- )
11
+ def initialize(url:, api_key:, index_name:, llm:, llm_api_key:)
12
+ depends_on "qdrant-ruby"
13
+ require "qdrant"
14
+
20
15
  @client = ::Qdrant::Client.new(
21
16
  url: url,
22
17
  api_key: api_key
@@ -29,15 +24,13 @@ module Vectorsearch
29
24
  # Add a list of texts to the index
30
25
  # @param texts [Array] The list of texts to add
31
26
  # @return [Hash] The response from the server
32
- def add_texts(
33
- texts:
34
- )
35
- batch = { ids: [], vectors: [], payloads: [] }
27
+ def add_texts(texts:)
28
+ batch = {ids: [], vectors: [], payloads: []}
36
29
 
37
30
  texts.each do |text|
38
31
  batch[:ids].push(SecureRandom.uuid)
39
32
  batch[:vectors].push(generate_embedding(text: text))
40
- batch[:payloads].push({ content: text })
33
+ batch[:payloads].push({content: text})
41
34
  end
42
35
 
43
36
  client.points.upsert(
@@ -106,4 +99,4 @@ module Vectorsearch
106
99
  generate_completion(prompt: prompt)
107
100
  end
108
101
  end
109
- end
102
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "weaviate"
4
-
5
3
  module Vectorsearch
6
4
  class Weaviate < Base
7
5
  # Initialize the Weaviate adapter
@@ -10,13 +8,10 @@ module Vectorsearch
10
8
  # @param index_name [String] The name of the index to use
11
9
  # @param llm [Symbol] The LLM to use
12
10
  # @param llm_api_key [String] The API key for the LLM
13
- def initialize(
14
- url:,
15
- api_key:,
16
- index_name:,
17
- llm:,
18
- llm_api_key:
19
- )
11
+ def initialize(url:, api_key:, index_name:, llm:, llm_api_key:)
12
+ depends_on "weaviate-ruby"
13
+ require "weaviate"
14
+
20
15
  @client = ::Weaviate::Client.new(
21
16
  url: url,
22
17
  api_key: api_key,
@@ -31,13 +26,11 @@ module Vectorsearch
31
26
  # Add a list of texts to the index
32
27
  # @param texts [Array] The list of texts to add
33
28
  # @return [Hash] The response from the server
34
- def add_texts(
35
- texts:
36
- )
29
+ def add_texts(texts:)
37
30
  objects = texts.map do |text|
38
31
  {
39
32
  class: index_name,
40
- properties: { content: text }
33
+ properties: {content: text}
41
34
  }
42
35
  end
43
36
 
@@ -50,7 +43,7 @@ module Vectorsearch
50
43
  def create_default_schema
51
44
  client.schema.create(
52
45
  class_name: index_name,
53
- vectorizer: "text2vec-#{llm.to_s}",
46
+ vectorizer: "text2vec-#{llm}",
54
47
  # TODO: Figure out a way to optionally enable it
55
48
  # "module_config": {
56
49
  # "qna-openai": {}
@@ -69,10 +62,7 @@ module Vectorsearch
69
62
  # @param query [String] The query to search for
70
63
  # @param k [Integer|String] The number of results to return
71
64
  # @return [Hash] The search results
72
- def similarity_search(
73
- query:,
74
- k: 4
75
- )
65
+ def similarity_search(query:, k: 4)
76
66
  near_text = "{ concepts: [\"#{query}\"] }"
77
67
 
78
68
  client.query.get(
@@ -87,10 +77,7 @@ module Vectorsearch
87
77
  # @param embedding [Array] The vector to search for
88
78
  # @param k [Integer|String] The number of results to return
89
79
  # @return [Hash] The search results
90
- def similarity_search_by_vector(
91
- embedding:,
92
- k: 4
93
- )
80
+ def similarity_search_by_vector(embedding:, k: 4)
94
81
  near_vector = "{ vector: #{embedding} }"
95
82
 
96
83
  client.query.get(
@@ -104,9 +91,7 @@ module Vectorsearch
104
91
  # Ask a question and return the answer
105
92
  # @param question [String] The question to ask
106
93
  # @return [Hash] The answer
107
- def ask(
108
- question:
109
- )
94
+ def ask(question:)
110
95
  # Weaviate currently supports the `ask:` parameter only for the OpenAI LLM (with `qna-openai` module enabled).
111
96
  # The Cohere support is on the way: https://github.com/weaviate/weaviate/pull/2600
112
97
  if llm == :openai
@@ -132,4 +117,4 @@ module Vectorsearch
132
117
  end
133
118
  end
134
119
  end
135
- end
120
+ end
data/lib/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Langchain
4
- VERSION = "0.3.2"
4
+ VERSION = "0.3.4"
5
5
  end