langchainrb 0.5.4 → 0.5.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.env.example +1 -0
  3. data/CHANGELOG.md +13 -0
  4. data/Gemfile.lock +10 -1
  5. data/README.md +8 -5
  6. data/examples/store_and_query_with_pinecone.rb +5 -4
  7. data/lib/langchain/agent/base.rb +5 -0
  8. data/lib/langchain/agent/chain_of_thought_agent/chain_of_thought_agent.rb +22 -10
  9. data/lib/langchain/agent/chain_of_thought_agent/chain_of_thought_agent_prompt.yaml +26 -0
  10. data/lib/langchain/agent/sql_query_agent/sql_query_agent.rb +7 -7
  11. data/lib/langchain/agent/sql_query_agent/sql_query_agent_answer_prompt.yaml +11 -0
  12. data/lib/langchain/agent/sql_query_agent/sql_query_agent_sql_prompt.yaml +21 -0
  13. data/lib/langchain/chunker/base.rb +15 -0
  14. data/lib/langchain/chunker/text.rb +38 -0
  15. data/lib/langchain/contextual_logger.rb +60 -0
  16. data/lib/langchain/conversation.rb +97 -0
  17. data/lib/langchain/data.rb +4 -0
  18. data/lib/langchain/llm/google_palm.rb +3 -2
  19. data/lib/langchain/llm/openai.rb +16 -6
  20. data/lib/langchain/llm/prompts/summarize_template.yaml +9 -0
  21. data/lib/langchain/llm/replicate.rb +1 -1
  22. data/lib/langchain/prompt/base.rb +2 -2
  23. data/lib/langchain/tool/base.rb +9 -3
  24. data/lib/langchain/tool/calculator.rb +2 -2
  25. data/lib/langchain/tool/database.rb +3 -3
  26. data/lib/langchain/tool/{serp_api.rb → google_search.rb} +9 -9
  27. data/lib/langchain/tool/ruby_code_interpreter.rb +1 -1
  28. data/lib/langchain/tool/weather.rb +67 -0
  29. data/lib/langchain/tool/wikipedia.rb +1 -1
  30. data/lib/langchain/utils/token_length/base_validator.rb +38 -0
  31. data/lib/langchain/utils/token_length/google_palm_validator.rb +9 -29
  32. data/lib/langchain/utils/token_length/openai_validator.rb +10 -27
  33. data/lib/langchain/utils/token_length/token_limit_exceeded.rb +17 -0
  34. data/lib/langchain/vectorsearch/base.rb +6 -0
  35. data/lib/langchain/vectorsearch/hnswlib.rb +2 -2
  36. data/lib/langchain/version.rb +1 -1
  37. data/lib/langchain.rb +29 -12
  38. metadata +46 -11
  39. data/lib/langchain/agent/chain_of_thought_agent/chain_of_thought_agent_prompt.json +0 -10
  40. data/lib/langchain/agent/sql_query_agent/sql_query_agent_answer_prompt.json +0 -10
  41. data/lib/langchain/agent/sql_query_agent/sql_query_agent_sql_prompt.json +0 -10
  42. data/lib/langchain/chat.rb +0 -50
  43. data/lib/langchain/llm/prompts/summarize_template.json +0 -5
@@ -17,6 +17,7 @@ module Langchain::LLM
17
17
  embeddings_model_name: "text-embedding-ada-002",
18
18
  dimension: 1536
19
19
  }.freeze
20
+ LENGTH_VALIDATOR = Langchain::Utils::TokenLength::OpenAIValidator
20
21
 
21
22
  def initialize(api_key:, llm_options: {})
22
23
  depends_on "ruby-openai"
@@ -35,7 +36,7 @@ module Langchain::LLM
35
36
  def embed(text:, **params)
36
37
  parameters = {model: DEFAULTS[:embeddings_model_name], input: text}
37
38
 
38
- Langchain::Utils::TokenLength::OpenAIValidator.validate_max_tokens!(text, parameters[:model])
39
+ validate_max_tokens(text, parameters[:model])
39
40
 
40
41
  response = client.embeddings(parameters: parameters.merge(params))
41
42
  response.dig("data").first.dig("embedding")
@@ -52,7 +53,7 @@ module Langchain::LLM
52
53
  parameters = compose_parameters DEFAULTS[:completion_model_name], params
53
54
 
54
55
  parameters[:prompt] = prompt
55
- parameters[:max_tokens] = Langchain::Utils::TokenLength::OpenAIValidator.validate_max_tokens!(prompt, parameters[:model])
56
+ parameters[:max_tokens] = validate_max_tokens(prompt, parameters[:model])
56
57
 
57
58
  response = client.completions(parameters: parameters)
58
59
  response.dig("choices", 0, "text")
@@ -66,6 +67,7 @@ module Langchain::LLM
66
67
  # @param context [String] The context of the conversation
67
68
  # @param examples [Array] Examples of messages provide model with
68
69
  # @param options extra parameters passed to OpenAI::Client#chat
70
+ # @param block [Block] Pass the block to stream the response
69
71
  # @return [String] The chat completion
70
72
  #
71
73
  def chat(prompt: "", messages: [], context: "", examples: [], **options)
@@ -75,11 +77,19 @@ module Langchain::LLM
75
77
  parameters[:messages] = compose_chat_messages(prompt: prompt, messages: messages, context: context, examples: examples)
76
78
  parameters[:max_tokens] = validate_max_tokens(parameters[:messages], parameters[:model])
77
79
 
80
+ if (streaming = block_given?)
81
+ parameters[:stream] = proc do |chunk, _bytesize|
82
+ yield chunk.dig("choices", 0, "delta", "content")
83
+ end
84
+ end
85
+
78
86
  response = client.chat(parameters: parameters)
79
87
 
80
- raise "Chat completion failed: #{response}" if response.dig("error")
88
+ raise "Chat completion failed: #{response}" if !response.empty? && response.dig("error")
81
89
 
82
- response.dig("choices", 0, "message", "content")
90
+ unless streaming
91
+ response.dig("choices", 0, "message", "content")
92
+ end
83
93
  end
84
94
 
85
95
  #
@@ -90,7 +100,7 @@ module Langchain::LLM
90
100
  #
91
101
  def summarize(text:)
92
102
  prompt_template = Langchain::Prompt.load_from_path(
93
- file_path: Langchain.root.join("langchain/llm/prompts/summarize_template.json")
103
+ file_path: Langchain.root.join("langchain/llm/prompts/summarize_template.yaml")
94
104
  )
95
105
  prompt = prompt_template.format(text: text)
96
106
 
@@ -140,7 +150,7 @@ module Langchain::LLM
140
150
  end
141
151
 
142
152
  def validate_max_tokens(messages, model)
143
- Langchain::Utils::TokenLength::OpenAIValidator.validate_max_tokens!(messages, model)
153
+ LENGTH_VALIDATOR.validate_max_tokens!(messages, model)
144
154
  end
145
155
  end
146
156
  end
@@ -0,0 +1,9 @@
1
+ _type: prompt
2
+ input_variables:
3
+ - text
4
+ template: |
5
+ Write a concise summary of the following:
6
+
7
+ {text}
8
+
9
+ CONCISE SUMMARY:
@@ -94,7 +94,7 @@ module Langchain::LLM
94
94
  #
95
95
  def summarize(text:)
96
96
  prompt_template = Langchain::Prompt.load_from_path(
97
- file_path: Langchain.root.join("langchain/llm/prompts/summarize_template.json")
97
+ file_path: Langchain.root.join("langchain/llm/prompts/summarize_template.yaml")
98
98
  )
99
99
  prompt = prompt_template.format(text: text)
100
100
 
@@ -45,11 +45,11 @@ module Langchain::Prompt
45
45
  end
46
46
 
47
47
  #
48
- # Save the object to a file in JSON format.
48
+ # Save the object to a file in JSON or YAML format.
49
49
  #
50
50
  # @param file_path [String, Pathname] The path to the file to save the object to
51
51
  #
52
- # @raise [ArgumentError] If file_path doesn't end with .json
52
+ # @raise [ArgumentError] If file_path doesn't end with .json or .yaml or .yml
53
53
  #
54
54
  # @return [void]
55
55
  #
@@ -9,7 +9,7 @@ module Langchain::Tool
9
9
  #
10
10
  # - {Langchain::Tool::Calculator}: Calculate the result of a math expression
11
11
  # - {Langchain::Tool::RubyCodeInterpretor}: Runs ruby code
12
- # - {Langchain::Tool::Search}: search on Google (via SerpAPI)
12
+ # - {Langchain::Tool::GoogleSearch}: search on Google (via SerpAPI)
13
13
  # - {Langchain::Tool::Wikipedia}: search on Wikipedia
14
14
  #
15
15
  # == Usage
@@ -30,13 +30,13 @@ module Langchain::Tool
30
30
  # agent = Langchain::Agent::ChainOfThoughtAgent.new(
31
31
  # llm: :openai, # or :cohere, :hugging_face, :google_palm or :replicate
32
32
  # llm_api_key: ENV["OPENAI_API_KEY"],
33
- # tools: ["search", "calculator", "wikipedia"]
33
+ # tools: ["google_search", "calculator", "wikipedia"]
34
34
  # )
35
35
  #
36
36
  # 4. Confirm that the Agent is using the Tools you passed in:
37
37
  #
38
38
  # agent.tools
39
- # # => ["search", "calculator", "wikipedia"]
39
+ # # => ["google_search", "calculator", "wikipedia"]
40
40
  #
41
41
  # == Adding Tools
42
42
  #
@@ -57,6 +57,12 @@ module Langchain::Tool
57
57
  self.class.const_get(:NAME)
58
58
  end
59
59
 
60
+ def self.logger_options
61
+ {
62
+ color: :light_blue
63
+ }
64
+ end
65
+
60
66
  #
61
67
  # Returns the DESCRIPTION constant of the tool
62
68
  #
@@ -28,14 +28,14 @@ module Langchain::Tool
28
28
  # @param input [String] math expression
29
29
  # @return [String] Answer
30
30
  def execute(input:)
31
- Langchain.logger.info("[#{self.class.name}]".light_blue + ": Executing \"#{input}\"")
31
+ Langchain.logger.info("Executing \"#{input}\"", for: self.class)
32
32
 
33
33
  Eqn::Calculator.calc(input)
34
34
  rescue Eqn::ParseError, Eqn::NoVariableValueError
35
35
  # Sometimes the input is not a pure math expression, e.g: "12F in Celsius"
36
36
  # We can use the google answer box to evaluate this expression
37
37
  # TODO: Figure out to find a better way to evaluate these language expressions.
38
- hash_results = Langchain::Tool::SerpApi
38
+ hash_results = Langchain::Tool::GoogleSearch
39
39
  .new(api_key: ENV["SERPAPI_API_KEY"])
40
40
  .execute_search(input: input)
41
41
  hash_results.dig(:answer_box, :to) ||
@@ -39,7 +39,7 @@ module Langchain::Tool
39
39
  # @return [String] schema
40
40
  #
41
41
  def schema
42
- Langchain.logger.info("[#{self.class.name}]".light_blue + ": Dumping schema")
42
+ Langchain.logger.info("Dumping schema", for: self.class)
43
43
  db.dump_schema_migration(same_db: true, indexes: false) unless db.adapter_scheme == :mock
44
44
  end
45
45
 
@@ -50,11 +50,11 @@ module Langchain::Tool
50
50
  # @return [Array] results
51
51
  #
52
52
  def execute(input:)
53
- Langchain.logger.info("[#{self.class.name}]".light_blue + ": Executing \"#{input}\"")
53
+ Langchain.logger.info("Executing \"#{input}\"", for: self.class)
54
54
 
55
55
  db[input].to_a
56
56
  rescue Sequel::DatabaseError => e
57
- Langchain.logger.error("[#{self.class.name}]".light_red + ": #{e.message}")
57
+ Langchain.logger.error(e.message, for: self.class)
58
58
  end
59
59
  end
60
60
  end
@@ -1,18 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Langchain::Tool
4
- class SerpApi < Base
4
+ class GoogleSearch < Base
5
5
  #
6
- # Wrapper around SerpAPI
6
+ # Wrapper around Google Serp SPI
7
7
  #
8
8
  # Gem requirements: gem "google_search_results", "~> 2.0.0"
9
9
  #
10
10
  # Usage:
11
- # search = Langchain::Tool::SerpApi.new(api_key: "YOUR_API_KEY")
11
+ # search = Langchain::Tool::GoogleSearch.new(api_key: "YOUR_API_KEY")
12
12
  # search.execute(input: "What is the capital of France?")
13
13
  #
14
14
 
15
- NAME = "search"
15
+ NAME = "google_search"
16
16
 
17
17
  description <<~DESC
18
18
  A wrapper around Google Search.
@@ -26,10 +26,10 @@ module Langchain::Tool
26
26
  attr_reader :api_key
27
27
 
28
28
  #
29
- # Initializes the SerpAPI tool
29
+ # Initializes the Google Search tool
30
30
  #
31
- # @param api_key [String] SerpAPI API key
32
- # @return [Langchain::Tool::SerpApi] SerpAPI tool
31
+ # @param api_key [String] Search API key
32
+ # @return [Langchain::Tool::GoogleSearch] Google search tool
33
33
  #
34
34
  def initialize(api_key:)
35
35
  depends_on "google_search_results"
@@ -54,7 +54,7 @@ module Langchain::Tool
54
54
  # @return [String] Answer
55
55
  #
56
56
  def execute(input:)
57
- Langchain.logger.info("[#{self.class.name}]".light_blue + ": Executing \"#{input}\"")
57
+ Langchain.logger.info("Executing \"#{input}\"", for: self.class)
58
58
 
59
59
  hash_results = execute_search(input: input)
60
60
 
@@ -72,7 +72,7 @@ module Langchain::Tool
72
72
  # @return [Hash] hash_results JSON
73
73
  #
74
74
  def execute_search(input:)
75
- GoogleSearch
75
+ ::GoogleSearch
76
76
  .new(q: input, serp_api_key: api_key)
77
77
  .get_hash
78
78
  end
@@ -21,7 +21,7 @@ module Langchain::Tool
21
21
  # @param input [String] ruby code expression
22
22
  # @return [String] Answer
23
23
  def execute(input:)
24
- Langchain.logger.info("[#{self.class.name}]".light_blue + ": Executing \"#{input}\"")
24
+ Langchain.logger.info("Executing \"#{input}\"", for: self.class)
25
25
 
26
26
  safe_eval(input)
27
27
  end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Langchain::Tool
4
+ class Weather < Base
5
+ #
6
+ # A weather tool that gets current weather data
7
+ #
8
+ # Current weather data is free for 1000 calls per day (https://home.openweathermap.org/api_keys)
9
+ # Forecast and historical data require registration with credit card, so not supported yet.
10
+ #
11
+ # Gem requirements:
12
+ # gem "open-weather-ruby-client", "~> 0.3.0"
13
+ # api_key: https://home.openweathermap.org/api_keys
14
+ #
15
+ # Usage:
16
+ # weather = Langchain::Tool::Weather.new(api_key: "YOUR_API_KEY")
17
+ # weather.execute(input: "Boston, MA; imperial")
18
+ #
19
+
20
+ NAME = "weather"
21
+
22
+ description <<~DESC
23
+ Useful for getting current weather data
24
+
25
+ The input to this tool should be a city name followed by the units (imperial, metric, or standard)
26
+ Usage:
27
+ Action Input: St Louis, Missouri; metric
28
+ Action Input: Boston, Massachusetts; imperial
29
+ Action Input: Dubai, AE; imperial
30
+ Action Input: Kiev, Ukraine; metric
31
+ DESC
32
+
33
+ attr_reader :client, :units
34
+
35
+ #
36
+ # Initializes the Weather tool
37
+ #
38
+ # @param api_key [String] Open Weather API key
39
+ # @return [Langchain::Tool::Weather] Weather tool
40
+ #
41
+ def initialize(api_key:, units: "metric")
42
+ depends_on "open-weather-ruby-client"
43
+ require "open-weather-ruby-client"
44
+
45
+ OpenWeather::Client.configure do |config|
46
+ config.api_key = api_key
47
+ config.user_agent = "Langchainrb Ruby Client"
48
+ end
49
+
50
+ @client = OpenWeather::Client.new
51
+ end
52
+
53
+ # Returns current weather for a city
54
+ # @param input [String] comma separated city and unit (optional: imperial, metric, or standard)
55
+ # @return [String] Answer
56
+ def execute(input:)
57
+ Langchain.logger.info("Executing for \"#{input}\"", for: self.class)
58
+
59
+ input_array = input.split(";")
60
+ city, units = *input_array.map(&:strip)
61
+
62
+ data = client.current_weather(city: city, units: units)
63
+ weather = data.main.map { |key, value| "#{key} #{value}" }.join(", ")
64
+ "The current weather in #{data.name} is #{weather}"
65
+ end
66
+ end
67
+ end
@@ -26,7 +26,7 @@ module Langchain::Tool
26
26
  # @param input [String] search query
27
27
  # @return [String] Answer
28
28
  def execute(input:)
29
- Langchain.logger.info("[#{self.class.name}]".light_blue + ": Executing \"#{input}\"")
29
+ Langchain.logger.info("Executing \"#{input}\"", for: self.class)
30
30
 
31
31
  page = ::Wikipedia.find(input)
32
32
  # It would be nice to figure out a way to provide page.content but the LLM token limit is an issue
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Langchain
4
+ module Utils
5
+ module TokenLength
6
+ #
7
+ # Calculate the `max_tokens:` parameter to be set by calculating the context length of the text minus the prompt length
8
+ #
9
+ # @param content [String | Array<String>] The text or array of texts to validate
10
+ # @param model_name [String] The model name to validate against
11
+ # @return [Integer] Whether the text is valid or not
12
+ # @raise [TokenLimitExceeded] If the text is too long
13
+ #
14
+ class BaseValidator
15
+ def self.validate_max_tokens!(content, model_name, options = {})
16
+ text_token_length = if content.is_a?(Array)
17
+ content.sum { |item| token_length(item.to_json, model_name, options) }
18
+ else
19
+ token_length(content, model_name, options)
20
+ end
21
+
22
+ leftover_tokens = token_limit(model_name) - text_token_length
23
+
24
+ # Raise an error even if whole prompt is equal to the model's token limit (leftover_tokens == 0)
25
+ if leftover_tokens <= 0
26
+ raise limit_exceeded_exception(token_limit(model_name), text_token_length)
27
+ end
28
+
29
+ leftover_tokens
30
+ end
31
+
32
+ def self.limit_exceeded_exception(limit, length)
33
+ TokenLimitExceeded.new("This model's maximum context length is #{limit} tokens, but the given text is #{length} tokens long.", length - limit)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -7,7 +7,7 @@ module Langchain
7
7
  # This class is meant to validate the length of the text passed in to Google Palm's API.
8
8
  # It is used to validate the token length before the API call is made
9
9
  #
10
- class GooglePalmValidator
10
+ class GooglePalmValidator < BaseValidator
11
11
  TOKEN_LIMITS = {
12
12
  # Source:
13
13
  # This data can be pulled when `list_models()` method is called: https://github.com/andreibondarev/google_palm_api#usage
@@ -26,43 +26,23 @@ module Langchain
26
26
  # }
27
27
  }.freeze
28
28
 
29
- #
30
- # Validate the context length of the text
31
- #
32
- # @param content [String | Array<String>] The text or array of texts to validate
33
- # @param model_name [String] The model name to validate against
34
- # @return [Integer] Whether the text is valid or not
35
- # @raise [TokenLimitExceeded] If the text is too long
36
- #
37
- def self.validate_max_tokens!(google_palm_llm, content, model_name)
38
- text_token_length = if content.is_a?(Array)
39
- content.sum { |item| token_length(google_palm_llm, item.to_json, model_name) }
40
- else
41
- token_length(google_palm_llm, content, model_name)
42
- end
43
-
44
- leftover_tokens = TOKEN_LIMITS.dig(model_name, "input_token_limit") - text_token_length
45
-
46
- # Raise an error even if whole prompt is equal to the model's token limit (leftover_tokens == 0)
47
- if leftover_tokens <= 0
48
- raise TokenLimitExceeded, "This model's maximum context length is #{TOKEN_LIMITS.dig(model_name, "input_token_limit")} tokens, but the given text is #{text_token_length} tokens long."
49
- end
50
-
51
- leftover_tokens
52
- end
53
-
54
29
  #
55
30
  # Calculate token length for a given text and model name
56
31
  #
57
- # @param llm [Langchain::LLM:GooglePalm] The Langchain::LLM:GooglePalm instance
58
32
  # @param text [String] The text to calculate the token length for
59
33
  # @param model_name [String] The model name to validate against
34
+ # @param options [Hash] the options to create a message with
35
+ # @option options [Langchain::LLM:GooglePalm] :llm The Langchain::LLM:GooglePalm instance
60
36
  # @return [Integer] The token length of the text
61
37
  #
62
- def self.token_length(llm, text, model_name = "chat-bison-001")
63
- response = llm.client.count_message_tokens(model: model_name, prompt: text)
38
+ def self.token_length(text, model_name = "chat-bison-001", options)
39
+ response = options[:llm].client.count_message_tokens(model: model_name, prompt: text)
64
40
  response.dig("tokenCount")
65
41
  end
42
+
43
+ def self.token_limit(model_name)
44
+ TOKEN_LIMITS.dig(model_name, "input_token_limit")
45
+ end
66
46
  end
67
47
  end
68
48
  end
@@ -9,7 +9,7 @@ module Langchain
9
9
  # This class is meant to validate the length of the text passed in to OpenAI's API.
10
10
  # It is used to validate the token length before the API call is made
11
11
  #
12
- class OpenAIValidator
12
+ class OpenAIValidator < BaseValidator
13
13
  TOKEN_LIMITS = {
14
14
  # Source:
15
15
  # https://platform.openai.com/docs/api-reference/embeddings
@@ -17,6 +17,9 @@ module Langchain
17
17
  "text-embedding-ada-002" => 8191,
18
18
  "gpt-3.5-turbo" => 4096,
19
19
  "gpt-3.5-turbo-0301" => 4096,
20
+ "gpt-3.5-turbo-0613" => 4096,
21
+ "gpt-3.5-turbo-16k" => 16384,
22
+ "gpt-3.5-turbo-16k-0613" => 16384,
20
23
  "text-davinci-003" => 4097,
21
24
  "text-davinci-002" => 4097,
22
25
  "code-davinci-002" => 8001,
@@ -24,6 +27,7 @@ module Langchain
24
27
  "gpt-4-0314" => 8192,
25
28
  "gpt-4-32k" => 32768,
26
29
  "gpt-4-32k-0314" => 32768,
30
+ "gpt-4-32k-0613" => 32768,
27
31
  "text-curie-001" => 2049,
28
32
  "text-babbage-001" => 2049,
29
33
  "text-ada-001" => 2049,
@@ -33,31 +37,6 @@ module Langchain
33
37
  "ada" => 2049
34
38
  }.freeze
35
39
 
36
- #
37
- # Calculate the `max_tokens:` parameter to be set by calculating the context length of the text minus the prompt length
38
- #
39
- # @param content [String | Array<String>] The text or array of texts to validate
40
- # @param model_name [String] The model name to validate against
41
- # @return [Integer] Whether the text is valid or not
42
- # @raise [TokenLimitExceeded] If the text is too long
43
- #
44
- def self.validate_max_tokens!(content, model_name)
45
- text_token_length = if content.is_a?(Array)
46
- content.sum { |item| token_length(item.to_json, model_name) }
47
- else
48
- token_length(content, model_name)
49
- end
50
-
51
- max_tokens = TOKEN_LIMITS[model_name] - text_token_length
52
-
53
- # Raise an error even if whole prompt is equal to the model's token limit (max_tokens == 0) since not response will be returned
54
- if max_tokens <= 0
55
- raise TokenLimitExceeded, "This model's maximum context length is #{TOKEN_LIMITS[model_name]} tokens, but the given text is #{text_token_length} tokens long."
56
- end
57
-
58
- max_tokens
59
- end
60
-
61
40
  #
62
41
  # Calculate token length for a given text and model name
63
42
  #
@@ -65,10 +44,14 @@ module Langchain
65
44
  # @param model_name [String] The model name to validate against
66
45
  # @return [Integer] The token length of the text
67
46
  #
68
- def self.token_length(text, model_name)
47
+ def self.token_length(text, model_name, options = {})
69
48
  encoder = Tiktoken.encoding_for_model(model_name)
70
49
  encoder.encode(text).length
71
50
  end
51
+
52
+ def self.token_limit(model_name)
53
+ TOKEN_LIMITS[model_name]
54
+ end
72
55
  end
73
56
  end
74
57
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Langchain
4
+ module Utils
5
+ module TokenLength
6
+ class TokenLimitExceeded < StandardError
7
+ attr_reader :token_overflow
8
+
9
+ def initialize(message = "", token_overflow = 0)
10
+ super message
11
+
12
+ @token_overflow = token_overflow
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -155,5 +155,11 @@ module Langchain::Vectorsearch
155
155
 
156
156
  add_texts(texts: texts)
157
157
  end
158
+
159
+ def self.logger_options
160
+ {
161
+ color: :blue
162
+ }
163
+ end
158
164
  end
159
165
  end
@@ -110,12 +110,12 @@ module Langchain::Vectorsearch
110
110
  if File.exist?(path_to_index)
111
111
  client.load_index(path_to_index)
112
112
 
113
- Langchain.logger.info("[#{self.class.name}]".blue + ": Successfully loaded the index at \"#{path_to_index}\"")
113
+ Langchain.logger.info("Successfully loaded the index at \"#{path_to_index}\"", for: self.class)
114
114
  else
115
115
  # Default max_elements: 100, but we constantly resize the index as new data is written to it
116
116
  client.init_index(max_elements: 100)
117
117
 
118
- Langchain.logger.info("[#{self.class.name}]".blue + ": Creating a new index at \"#{path_to_index}\"")
118
+ Langchain.logger.info("Creating a new index at \"#{path_to_index}\"", for: self.class)
119
119
  end
120
120
  end
121
121
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Langchain
4
- VERSION = "0.5.4"
4
+ VERSION = "0.5.6"
5
5
  end
data/lib/langchain.rb CHANGED
@@ -46,36 +46,49 @@ require_relative "./langchain/version"
46
46
  #
47
47
  # LangChain.rb uses standard logging mechanisms and defaults to :debug level. Most messages are at info level, but we will add debug or warn statements as needed. To show all log messages:
48
48
  #
49
- # Langchain.logger.level = :info
49
+ # Langchain.logger.level = :info
50
50
  module Langchain
51
+ autoload :Loader, "langchain/loader"
52
+ autoload :Data, "langchain/data"
53
+ autoload :Conversation, "langchain/conversation"
54
+ autoload :DependencyHelper, "langchain/dependency_helper"
55
+ autoload :ContextualLogger, "langchain/contextual_logger"
56
+
51
57
  class << self
52
- # @return [Logger]
53
- attr_accessor :logger
58
+ # @return [ContextualLogger]
59
+ attr_reader :logger
60
+
61
+ # @param logger [Logger]
62
+ # @return [ContextualLogger]
63
+ def logger=(logger)
64
+ @logger = ContextualLogger.new(logger)
65
+ end
54
66
 
55
67
  # @return [Pathname]
56
68
  attr_reader :root
57
69
  end
58
70
 
59
- @logger ||= ::Logger.new($stdout, level: :warn, formatter: ->(severity, datetime, progname, msg) { "[LangChain.rb]".yellow + " #{msg}\n" })
71
+ self.logger ||= ::Logger.new($stdout, level: :warn)
60
72
 
61
73
  @root = Pathname.new(__dir__)
62
74
 
63
- autoload :Loader, "langchain/loader"
64
- autoload :Data, "langchain/data"
65
- autoload :Chat, "langchain/chat"
66
- autoload :DependencyHelper, "langchain/dependency_helper"
67
-
68
75
  module Agent
69
76
  autoload :Base, "langchain/agent/base"
70
77
  autoload :ChainOfThoughtAgent, "langchain/agent/chain_of_thought_agent/chain_of_thought_agent.rb"
71
78
  autoload :SQLQueryAgent, "langchain/agent/sql_query_agent/sql_query_agent.rb"
72
79
  end
73
80
 
81
+ module Chunker
82
+ autoload :Base, "langchain/chunker/base"
83
+ autoload :Text, "langchain/chunker/text"
84
+ end
85
+
74
86
  module Tool
75
87
  autoload :Base, "langchain/tool/base"
76
88
  autoload :Calculator, "langchain/tool/calculator"
77
89
  autoload :RubyCodeInterpreter, "langchain/tool/ruby_code_interpreter"
78
- autoload :SerpApi, "langchain/tool/serp_api"
90
+ autoload :GoogleSearch, "langchain/tool/google_search"
91
+ autoload :Weather, "langchain/tool/weather"
79
92
  autoload :Wikipedia, "langchain/tool/wikipedia"
80
93
  autoload :Database, "langchain/tool/database"
81
94
  end
@@ -94,8 +107,8 @@ module Langchain
94
107
 
95
108
  module Utils
96
109
  module TokenLength
97
- class TokenLimitExceeded < StandardError; end
98
-
110
+ autoload :BaseValidator, "langchain/utils/token_length/base_validator"
111
+ autoload :TokenLimitExceeded, "langchain/utils/token_length/token_limit_exceeded"
99
112
  autoload :OpenAIValidator, "langchain/utils/token_length/openai_validator"
100
113
  autoload :GooglePalmValidator, "langchain/utils/token_length/google_palm_validator"
101
114
  end
@@ -129,4 +142,8 @@ module Langchain
129
142
  autoload :PromptTemplate, "langchain/prompt/prompt_template"
130
143
  autoload :FewShotPromptTemplate, "langchain/prompt/few_shot_prompt_template"
131
144
  end
145
+
146
+ module Errors
147
+ class BaseError < StandardError; end
148
+ end
132
149
  end