langchainrb 0.7.5 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +78 -0
  3. data/README.md +113 -56
  4. data/lib/langchain/assistants/assistant.rb +213 -0
  5. data/lib/langchain/assistants/message.rb +58 -0
  6. data/lib/langchain/assistants/thread.rb +34 -0
  7. data/lib/langchain/chunker/markdown.rb +37 -0
  8. data/lib/langchain/chunker/recursive_text.rb +0 -2
  9. data/lib/langchain/chunker/semantic.rb +1 -3
  10. data/lib/langchain/chunker/sentence.rb +0 -2
  11. data/lib/langchain/chunker/text.rb +0 -2
  12. data/lib/langchain/contextual_logger.rb +1 -1
  13. data/lib/langchain/data.rb +4 -3
  14. data/lib/langchain/llm/ai21.rb +1 -1
  15. data/lib/langchain/llm/anthropic.rb +86 -11
  16. data/lib/langchain/llm/aws_bedrock.rb +52 -0
  17. data/lib/langchain/llm/azure.rb +10 -97
  18. data/lib/langchain/llm/base.rb +3 -2
  19. data/lib/langchain/llm/cohere.rb +5 -7
  20. data/lib/langchain/llm/google_palm.rb +4 -2
  21. data/lib/langchain/llm/google_vertex_ai.rb +151 -0
  22. data/lib/langchain/llm/hugging_face.rb +1 -1
  23. data/lib/langchain/llm/llama_cpp.rb +18 -16
  24. data/lib/langchain/llm/mistral_ai.rb +68 -0
  25. data/lib/langchain/llm/ollama.rb +209 -27
  26. data/lib/langchain/llm/openai.rb +138 -170
  27. data/lib/langchain/llm/prompts/ollama/summarize_template.yaml +9 -0
  28. data/lib/langchain/llm/replicate.rb +1 -7
  29. data/lib/langchain/llm/response/anthropic_response.rb +20 -0
  30. data/lib/langchain/llm/response/base_response.rb +7 -0
  31. data/lib/langchain/llm/response/google_palm_response.rb +4 -0
  32. data/lib/langchain/llm/response/google_vertex_ai_response.rb +33 -0
  33. data/lib/langchain/llm/response/llama_cpp_response.rb +13 -0
  34. data/lib/langchain/llm/response/mistral_ai_response.rb +39 -0
  35. data/lib/langchain/llm/response/ollama_response.rb +27 -1
  36. data/lib/langchain/llm/response/openai_response.rb +8 -0
  37. data/lib/langchain/loader.rb +3 -2
  38. data/lib/langchain/output_parsers/base.rb +0 -4
  39. data/lib/langchain/output_parsers/output_fixing_parser.rb +7 -14
  40. data/lib/langchain/output_parsers/structured_output_parser.rb +0 -10
  41. data/lib/langchain/processors/csv.rb +37 -3
  42. data/lib/langchain/processors/eml.rb +64 -0
  43. data/lib/langchain/processors/markdown.rb +17 -0
  44. data/lib/langchain/processors/pptx.rb +29 -0
  45. data/lib/langchain/prompt/loading.rb +1 -1
  46. data/lib/langchain/tool/base.rb +21 -53
  47. data/lib/langchain/tool/calculator/calculator.json +19 -0
  48. data/lib/langchain/tool/{calculator.rb → calculator/calculator.rb} +8 -16
  49. data/lib/langchain/tool/database/database.json +46 -0
  50. data/lib/langchain/tool/database/database.rb +99 -0
  51. data/lib/langchain/tool/file_system/file_system.json +57 -0
  52. data/lib/langchain/tool/file_system/file_system.rb +32 -0
  53. data/lib/langchain/tool/google_search/google_search.json +19 -0
  54. data/lib/langchain/tool/{google_search.rb → google_search/google_search.rb} +5 -15
  55. data/lib/langchain/tool/ruby_code_interpreter/ruby_code_interpreter.json +19 -0
  56. data/lib/langchain/tool/{ruby_code_interpreter.rb → ruby_code_interpreter/ruby_code_interpreter.rb} +8 -4
  57. data/lib/langchain/tool/vectorsearch/vectorsearch.json +24 -0
  58. data/lib/langchain/tool/vectorsearch/vectorsearch.rb +36 -0
  59. data/lib/langchain/tool/weather/weather.json +19 -0
  60. data/lib/langchain/tool/{weather.rb → weather/weather.rb} +3 -15
  61. data/lib/langchain/tool/wikipedia/wikipedia.json +19 -0
  62. data/lib/langchain/tool/{wikipedia.rb → wikipedia/wikipedia.rb} +9 -9
  63. data/lib/langchain/utils/token_length/ai21_validator.rb +6 -2
  64. data/lib/langchain/utils/token_length/base_validator.rb +1 -1
  65. data/lib/langchain/utils/token_length/cohere_validator.rb +6 -2
  66. data/lib/langchain/utils/token_length/google_palm_validator.rb +5 -1
  67. data/lib/langchain/utils/token_length/openai_validator.rb +55 -1
  68. data/lib/langchain/utils/token_length/token_limit_exceeded.rb +1 -1
  69. data/lib/langchain/vectorsearch/base.rb +11 -4
  70. data/lib/langchain/vectorsearch/chroma.rb +10 -1
  71. data/lib/langchain/vectorsearch/elasticsearch.rb +53 -4
  72. data/lib/langchain/vectorsearch/epsilla.rb +149 -0
  73. data/lib/langchain/vectorsearch/hnswlib.rb +5 -1
  74. data/lib/langchain/vectorsearch/milvus.rb +4 -2
  75. data/lib/langchain/vectorsearch/pgvector.rb +14 -4
  76. data/lib/langchain/vectorsearch/pinecone.rb +8 -5
  77. data/lib/langchain/vectorsearch/qdrant.rb +16 -4
  78. data/lib/langchain/vectorsearch/weaviate.rb +20 -2
  79. data/lib/langchain/version.rb +1 -1
  80. data/lib/langchain.rb +20 -5
  81. metadata +182 -45
  82. data/lib/langchain/agent/agents.md +0 -54
  83. data/lib/langchain/agent/base.rb +0 -20
  84. data/lib/langchain/agent/react_agent/react_agent_prompt.yaml +0 -26
  85. data/lib/langchain/agent/react_agent.rb +0 -131
  86. data/lib/langchain/agent/sql_query_agent/sql_query_agent_answer_prompt.yaml +0 -11
  87. data/lib/langchain/agent/sql_query_agent/sql_query_agent_sql_prompt.yaml +0 -21
  88. data/lib/langchain/agent/sql_query_agent.rb +0 -82
  89. data/lib/langchain/conversation/context.rb +0 -8
  90. data/lib/langchain/conversation/memory.rb +0 -86
  91. data/lib/langchain/conversation/message.rb +0 -48
  92. data/lib/langchain/conversation/prompt.rb +0 -8
  93. data/lib/langchain/conversation/response.rb +0 -8
  94. data/lib/langchain/conversation.rb +0 -93
  95. data/lib/langchain/tool/database.rb +0 -90
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Langchain::Tool
4
+ class FileSystem < Base
5
+ #
6
+ # A tool that wraps the Ruby file system classes.
7
+ #
8
+ # Usage:
9
+ # file_system = Langchain::Tool::FileSystem.new
10
+ #
11
+ NAME = "file_system"
12
+ ANNOTATIONS_PATH = Langchain.root.join("./langchain/tool/#{NAME}/#{NAME}.json").to_path
13
+
14
+ def list_directory(directory_path:)
15
+ Dir.entries(directory_path)
16
+ rescue Errno::ENOENT
17
+ "No such directory: #{directory_path}"
18
+ end
19
+
20
+ def read_file(file_path:)
21
+ File.read(file_path)
22
+ rescue Errno::ENOENT
23
+ "No such file: #{file_path}"
24
+ end
25
+
26
+ def write_to_file(file_path:, content:)
27
+ File.write(file_path, content)
28
+ rescue Errno::EACCES
29
+ "Permission denied: #{file_path}"
30
+ end
31
+ end
32
+ 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,23 +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
-
17
- description <<~DESC
18
- A wrapper around SerpApi's Google Search API.
19
-
20
- Useful for when you need to answer questions about current events.
21
- Always one of the first options when you need to find information on internet.
22
-
23
- Input should be a search query.
24
- DESC
16
+ ANNOTATIONS_PATH = Langchain.root.join("./langchain/tool/#{NAME}/#{NAME}.json").to_path
25
17
 
26
18
  attr_reader :api_key
27
19
 
@@ -47,12 +39,10 @@ module Langchain::Tool
47
39
  new.execute_search(input: input)
48
40
  end
49
41
 
50
- #
51
42
  # Executes Google Search and returns the result
52
43
  #
53
44
  # @param input [String] search query
54
45
  # @return [String] Answer
55
- #
56
46
  def execute(input:)
57
47
  Langchain.logger.info("Executing \"#{input}\"", for: self.class)
58
48
 
@@ -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,12 +5,14 @@ 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"
11
- description <<~DESC
12
- 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
- DESC
15
+ ANNOTATIONS_PATH = Langchain.root.join("./langchain/tool/#{NAME}/#{NAME}.json").to_path
14
16
 
15
17
  def initialize(timeout: 30)
16
18
  depends_on "safe_ruby"
@@ -18,6 +20,8 @@ module Langchain::Tool
18
20
  @timeout = timeout
19
21
  end
20
22
 
23
+ # Executes Ruby code in a sandboxes environment.
24
+ #
21
25
  # @param input [String] ruby code expression
22
26
  # @return [String] Answer
23
27
  def execute(input:)
@@ -0,0 +1,24 @@
1
+ [
2
+ {
3
+ "type": "function",
4
+ "function": {
5
+ "name": "vectorsearch-similarity_search",
6
+ "description": "Vectorsearch: Retrieves relevant document for the query",
7
+ "parameters": {
8
+ "type": "object",
9
+ "properties": {
10
+ "query": {
11
+ "type": "string",
12
+ "description": "Query to find similar documents for"
13
+ },
14
+ "k": {
15
+ "type": "integer",
16
+ "description": "Number of similar documents to retrieve",
17
+ "default": 4
18
+ }
19
+ },
20
+ "required": ["query"]
21
+ }
22
+ }
23
+ }
24
+ ]
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Langchain::Tool
4
+ class Vectorsearch < Base
5
+ #
6
+ # A tool wraps vectorsearch classes
7
+ #
8
+ # Usage:
9
+ # # Initialize the LLM that will be used to generate embeddings
10
+ # ollama = Langchain::LLM::Ollama.new(url: ENV["OLLAMA_URL"]
11
+ # chroma = Langchain::Vectorsearch::Chroma.new(url: ENV["CHROMA_URL"], index_name: "my_index", llm: ollama)
12
+ #
13
+ # # This tool can now be used by the Assistant
14
+ # vectorsearch_tool = Langchain::Tool::Vectorsearch.new(vectorsearch: chroma)
15
+ #
16
+ NAME = "vectorsearch"
17
+ ANNOTATIONS_PATH = Langchain.root.join("./langchain/tool/#{NAME}/#{NAME}.json").to_path
18
+
19
+ attr_reader :vectorsearch
20
+
21
+ # Initializes the Vectorsearch tool
22
+ #
23
+ # @param vectorsearch [Langchain::Vectorsearch::Base] Vectorsearch instance to use
24
+ def initialize(vectorsearch:)
25
+ @vectorsearch = vectorsearch
26
+ end
27
+
28
+ # Executes the vector search and returns the results
29
+ #
30
+ # @param query [String] The query to search for
31
+ # @param k [Integer] The number of results to return
32
+ def similarity_search(query:, k: 4)
33
+ vectorsearch.similarity_search(query:, k: 4)
34
+ end
35
+ end
36
+ end
@@ -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,31 +13,18 @@ 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"
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
20
+ ANNOTATIONS_PATH = Langchain.root.join("./langchain/tool/#{NAME}/#{NAME}.json").to_path
32
21
 
33
22
  attr_reader :client, :units
34
23
 
35
- #
36
24
  # Initializes the Weather tool
37
25
  #
38
26
  # @param api_key [String] Open Weather API key
39
27
  # @return [Langchain::Tool::Weather] Weather tool
40
- #
41
28
  def initialize(api_key:, units: "metric")
42
29
  depends_on "open-weather-ruby-client"
43
30
  require "open-weather-ruby-client"
@@ -51,6 +38,7 @@ module Langchain::Tool
51
38
  end
52
39
 
53
40
  # Returns current weather for a city
41
+ #
54
42
  # @param input [String] comma separated city and unit (optional: imperial, metric, or standard)
55
43
  # @return [String] Answer
56
44
  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,23 +5,23 @@ 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"
11
- description <<~DESC
12
- A wrapper around Wikipedia.
13
-
14
- Useful for when you need to answer general questions about
15
- people, places, companies, facts, historical events, or other subjects.
16
-
17
- Input should be a search query.
18
- DESC
16
+ ANNOTATIONS_PATH = Langchain.root.join("./langchain/tool/#{NAME}/#{NAME}.json").to_path
19
17
 
18
+ # Initializes the Wikipedia tool
20
19
  def initialize
21
20
  depends_on "wikipedia-client", req: "wikipedia"
22
21
  end
23
22
 
24
23
  # Executes Wikipedia API search and returns the answer
24
+ #
25
25
  # @param input [String] search query
26
26
  # @return [String] Answer
27
27
  def execute(input:)
@@ -22,8 +22,8 @@ module Langchain
22
22
  # @param model_name [String] The model name to validate against
23
23
  # @return [Integer] The token length of the text
24
24
  #
25
- def self.token_length(text, model_name, client)
26
- res = client.tokenize(text)
25
+ def self.token_length(text, model_name, options = {})
26
+ res = options[:llm].tokenize(text)
27
27
  res.dig(:tokens).length
28
28
  end
29
29
 
@@ -31,6 +31,10 @@ module Langchain
31
31
  TOKEN_LIMITS[model_name]
32
32
  end
33
33
  singleton_class.alias_method :completion_token_limit, :token_limit
34
+
35
+ def self.token_length_from_messages(messages, model_name, options)
36
+ messages.sum { |message| token_length(message.to_json, model_name, options) }
37
+ end
34
38
  end
35
39
  end
36
40
  end
@@ -14,7 +14,7 @@ module Langchain
14
14
  class BaseValidator
15
15
  def self.validate_max_tokens!(content, model_name, options = {})
16
16
  text_token_length = if content.is_a?(Array)
17
- content.sum { |item| token_length(item.to_json, model_name, options) }
17
+ token_length_from_messages(content, model_name, options)
18
18
  else
19
19
  token_length(content, model_name, options)
20
20
  end
@@ -30,8 +30,8 @@ module Langchain
30
30
  # @param model_name [String] The model name to validate against
31
31
  # @return [Integer] The token length of the text
32
32
  #
33
- def self.token_length(text, model_name, client)
34
- res = client.tokenize(text: text)
33
+ def self.token_length(text, model_name, options = {})
34
+ res = options[:llm].tokenize(text: text)
35
35
  res["tokens"].length
36
36
  end
37
37
 
@@ -39,6 +39,10 @@ module Langchain
39
39
  TOKEN_LIMITS[model_name]
40
40
  end
41
41
  singleton_class.alias_method :completion_token_limit, :token_limit
42
+
43
+ def self.token_length_from_messages(messages, model_name, options)
44
+ messages.sum { |message| token_length(message.to_json, model_name, options) }
45
+ end
42
46
  end
43
47
  end
44
48
  end
@@ -35,7 +35,7 @@ module Langchain
35
35
  # @option options [Langchain::LLM:GooglePalm] :llm The Langchain::LLM:GooglePalm instance
36
36
  # @return [Integer] The token length of the text
37
37
  #
38
- def self.token_length(text, model_name = "chat-bison-001", options)
38
+ def self.token_length(text, model_name = "chat-bison-001", options = {})
39
39
  response = options[:llm].client.count_message_tokens(model: model_name, prompt: text)
40
40
 
41
41
  raise Langchain::LLM::ApiError.new(response["error"]["message"]) unless response["error"].nil?
@@ -43,6 +43,10 @@ module Langchain
43
43
  response.dig("tokenCount")
44
44
  end
45
45
 
46
+ def self.token_length_from_messages(messages, model_name, options = {})
47
+ messages.sum { |message| token_length(message.to_json, model_name, options) }
48
+ end
49
+
46
50
  def self.token_limit(model_name)
47
51
  TOKEN_LIMITS.dig(model_name, "input_token_limit")
48
52
  end
@@ -15,17 +15,23 @@ module Langchain
15
15
  # Source:
16
16
  # https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo
17
17
  "gpt-4-1106-preview" => 4096,
18
- "gpt-4-vision-preview" => 4096
18
+ "gpt-4-vision-preview" => 4096,
19
+ "gpt-3.5-turbo-1106" => 4096
19
20
  }
20
21
 
22
+ # NOTE: The gpt-4-turbo-preview is an alias that will always point to the latest GPT 4 Turbo preview
23
+ # the future previews may have a different token limit!
21
24
  TOKEN_LIMITS = {
22
25
  # Source:
23
26
  # https://platform.openai.com/docs/api-reference/embeddings
24
27
  # https://platform.openai.com/docs/models/gpt-4
28
+ "text-embedding-3-large" => 8191,
29
+ "text-embedding-3-small" => 8191,
25
30
  "text-embedding-ada-002" => 8191,
26
31
  "gpt-3.5-turbo" => 4096,
27
32
  "gpt-3.5-turbo-0301" => 4096,
28
33
  "gpt-3.5-turbo-0613" => 4096,
34
+ "gpt-3.5-turbo-1106" => 16385,
29
35
  "gpt-3.5-turbo-16k" => 16384,
30
36
  "gpt-3.5-turbo-16k-0613" => 16384,
31
37
  "text-davinci-003" => 4097,
@@ -38,6 +44,8 @@ module Langchain
38
44
  "gpt-4-32k-0314" => 32768,
39
45
  "gpt-4-32k-0613" => 32768,
40
46
  "gpt-4-1106-preview" => 128000,
47
+ "gpt-4-turbo-preview" => 128000,
48
+ "gpt-4-0125-preview" => 128000,
41
49
  "gpt-4-vision-preview" => 128000,
42
50
  "text-curie-001" => 2049,
43
51
  "text-babbage-001" => 2049,
@@ -56,6 +64,11 @@ module Langchain
56
64
  # @return [Integer] The token length of the text
57
65
  #
58
66
  def self.token_length(text, model_name, options = {})
67
+ # tiktoken-ruby doesn't support text-embedding-3-large or text-embedding-3-small yet
68
+ if ["text-embedding-3-large", "text-embedding-3-small"].include?(model_name)
69
+ model_name = "text-embedding-ada-002"
70
+ end
71
+
59
72
  encoder = Tiktoken.encoding_for_model(model_name)
60
73
  encoder.encode(text).length
61
74
  end
@@ -73,6 +86,47 @@ module Langchain
73
86
  max_tokens = super(content, model_name, options)
74
87
  [options[:max_tokens], max_tokens].reject(&:nil?).min
75
88
  end
89
+
90
+ # Copied from https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb
91
+ # Return the number of tokens used by a list of messages
92
+ #
93
+ # @param messages [Array<Hash>] The messages to calculate the token length for
94
+ # @param model [String] The model name to validate against
95
+ # @return [Integer] The token length of the messages
96
+ #
97
+ def self.token_length_from_messages(messages, model_name, options = {})
98
+ encoding = Tiktoken.encoding_for_model(model_name)
99
+
100
+ if ["gpt-3.5-turbo-0613", "gpt-3.5-turbo-16k-0613", "gpt-4-0314", "gpt-4-32k-0314", "gpt-4-0613", "gpt-4-32k-0613"].include?(model_name)
101
+ tokens_per_message = 3
102
+ tokens_per_name = 1
103
+ elsif model_name == "gpt-3.5-turbo-0301"
104
+ tokens_per_message = 4 # every message follows {role/name}\n{content}\n
105
+ tokens_per_name = -1 # if there's a name, the role is omitted
106
+ elsif model_name.include?("gpt-3.5-turbo")
107
+ # puts "Warning: gpt-3.5-turbo may update over time. Returning num tokens assuming gpt-3.5-turbo-0613."
108
+ return token_length_from_messages(messages, "gpt-3.5-turbo-0613", options)
109
+ elsif model_name.include?("gpt-4")
110
+ # puts "Warning: gpt-4 may update over time. Returning num tokens assuming gpt-4-0613."
111
+ return token_length_from_messages(messages, "gpt-4-0613", options)
112
+ else
113
+ raise NotImplementedError.new(
114
+ "token_length_from_messages() is not implemented for model #{model_name}. See https://github.com/openai/openai-python/blob/main/chatml.md for information on how messages are converted to tokens."
115
+ )
116
+ end
117
+
118
+ num_tokens = 0
119
+ messages.each do |message|
120
+ num_tokens += tokens_per_message
121
+ message.each do |key, value|
122
+ num_tokens += encoding.encode(value).length
123
+ num_tokens += tokens_per_name if ["name", :name].include?(key)
124
+ end
125
+ end
126
+
127
+ num_tokens += 3 # every reply is primed with assistant
128
+ num_tokens
129
+ end
76
130
  end
77
131
  end
78
132
  end
@@ -7,7 +7,7 @@ module Langchain
7
7
  attr_reader :token_overflow
8
8
 
9
9
  def initialize(message = "", token_overflow = 0)
10
- super message
10
+ super(message)
11
11
 
12
12
  @token_overflow = token_overflow
13
13
  end
@@ -7,6 +7,7 @@ module Langchain::Vectorsearch
7
7
  # == Available vector databases
8
8
  #
9
9
  # - {Langchain::Vectorsearch::Chroma}
10
+ # - {Langchain::Vectorsearch::Epsilla}
10
11
  # - {Langchain::Vectorsearch::Elasticsearch}
11
12
  # - {Langchain::Vectorsearch::Hnswlib}
12
13
  # - {Langchain::Vectorsearch::Milvus}
@@ -29,10 +30,11 @@ module Langchain::Vectorsearch
29
30
  # )
30
31
  #
31
32
  # # You can instantiate other supported vector databases the same way:
33
+ # epsilla = Langchain::Vectorsearch::Epsilla.new(...)
32
34
  # milvus = Langchain::Vectorsearch::Milvus.new(...)
33
35
  # qdrant = Langchain::Vectorsearch::Qdrant.new(...)
34
36
  # pinecone = Langchain::Vectorsearch::Pinecone.new(...)
35
- # chrome = Langchain::Vectorsearch::Chroma.new(...)
37
+ # chroma = Langchain::Vectorsearch::Chroma.new(...)
36
38
  # pgvector = Langchain::Vectorsearch::Pgvector.new(...)
37
39
  #
38
40
  # == Schema Creation
@@ -122,6 +124,11 @@ module Langchain::Vectorsearch
122
124
  raise NotImplementedError, "#{self.class.name} does not support updating texts"
123
125
  end
124
126
 
127
+ # Method supported by Vectorsearch DB to delete a list of texts from the index
128
+ def remove_texts(...)
129
+ raise NotImplementedError, "#{self.class.name} does not support deleting texts"
130
+ end
131
+
125
132
  # Method supported by Vectorsearch DB to search for similar texts in the index
126
133
  def similarity_search(...)
127
134
  raise NotImplementedError, "#{self.class.name} does not support similarity search"
@@ -134,7 +141,7 @@ module Langchain::Vectorsearch
134
141
  # @param k [Integer] The number of results to return
135
142
  # @return [String] Response
136
143
  def similarity_search_with_hyde(query:, k: 4)
137
- hyde_completion = llm.complete(prompt: generate_hyde_prompt(question: query))
144
+ hyde_completion = llm.complete(prompt: generate_hyde_prompt(question: query)).completion
138
145
  similarity_search(query: hyde_completion, k: k)
139
146
  end
140
147
 
@@ -173,13 +180,13 @@ module Langchain::Vectorsearch
173
180
  prompt_template.format(question: question, context: context)
174
181
  end
175
182
 
176
- def add_data(paths:)
183
+ def add_data(paths:, options: {}, chunker: Langchain::Chunker::Text)
177
184
  raise ArgumentError, "Paths must be provided" if Array(paths).empty?
178
185
 
179
186
  texts = Array(paths)
180
187
  .flatten
181
188
  .map do |path|
182
- data = Langchain::Loader.new(path)&.load&.chunks
189
+ data = Langchain::Loader.new(path, options, chunker: chunker)&.load&.chunks
183
190
  data.map { |chunk| chunk.text }
184
191
  end
185
192
 
@@ -60,6 +60,13 @@ module Langchain::Vectorsearch
60
60
  collection.update(embeddings)
61
61
  end
62
62
 
63
+ # Remove a list of texts from the index
64
+ # @param ids [Array<String>] The list of ids to remove
65
+ # @return [Hash] The response from the server
66
+ def remove_texts(ids:)
67
+ collection.delete(ids)
68
+ end
69
+
63
70
  # Create the collection with the default schema
64
71
  # @return [::Chroma::Resources::Collection] Created collection
65
72
  def create_default_schema
@@ -126,7 +133,9 @@ module Langchain::Vectorsearch
126
133
 
127
134
  prompt = generate_rag_prompt(question: question, context: context)
128
135
 
129
- response = llm.chat(prompt: prompt, &block)
136
+ messages = [{role: "user", content: prompt}]
137
+ response = llm.chat(messages: messages, &block)
138
+
130
139
  response.context = context
131
140
  response
132
141
  end